Skip to content

Commit 01e42c1

Browse files
committed
Fix @request-uri on the server side
1 parent 9f59439 commit 01e42c1

File tree

5 files changed

+150
-23
lines changed

5 files changed

+150
-23
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/yaronf/httpsign
33
go 1.17
44

55
require (
6+
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
67
github.com/dunglas/httpsfv v0.1.1
78
github.com/lestrrat-go/jwx v1.2.18
89
)
@@ -16,5 +17,6 @@ require (
1617
github.com/lestrrat-go/iter v1.0.1 // indirect
1718
github.com/lestrrat-go/option v1.0.0 // indirect
1819
github.com/pkg/errors v0.9.1 // indirect
20+
github.com/sergi/go-diff v1.2.0 // indirect
1921
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 // indirect
2022
)

go.sum

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
1+
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
2+
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
23
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
36
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
47
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
58
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
69
github.com/dunglas/httpsfv v0.1.1 h1:iV2PWNlj9Qbk+5I3fxPDJPTh9eM0rskrI1qdUVUNK1E=
710
github.com/dunglas/httpsfv v0.1.1/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
811
github.com/goccy/go-json v0.9.4 h1:L8MLKG2mvVXiQu07qB6hmfqeSYQdOnqPot2GhsIwIaI=
912
github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
13+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
14+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
15+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1016
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
1117
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
1218
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
@@ -23,7 +29,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
2329
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
2430
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2531
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
32+
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
33+
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
2634
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
35+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
2736
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
2837
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
2938
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
@@ -37,6 +46,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
3746
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
3847
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3948
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
49+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4050
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
51+
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
4152
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
4253
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

http2_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package httpsign
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"github.com/andreyvit/diff"
7+
"io"
8+
"net/http"
9+
"net/http/httptest"
10+
"strconv"
11+
"strings"
12+
"testing"
13+
"text/template"
14+
)
15+
16+
var httpreq5 = ``
17+
18+
var wantFields = `"kuku": my awesome header
19+
"@query": ?k1=v1&k2
20+
"@method": GET
21+
"@target-uri": http://127.0.0.1:{{.Port}}/?k1=v1&k2
22+
"@signature-params": ("kuku" "@query" "@method" "@target-uri");alg="hmac-sha256";keyid="key1"`
23+
24+
func execTemplate(t template.Template, name string, data interface{}) (string, error) {
25+
buf := &bytes.Buffer{}
26+
err := t.ExecuteTemplate(buf, name, data)
27+
return buf.String(), err
28+
}
29+
30+
func newClientRequest(t *testing.T, method, url, body string) *http.Request {
31+
in := strings.NewReader(body)
32+
req, err := http.NewRequest(method, url, bufio.NewReader(in))
33+
if err != nil {
34+
t.Errorf("could not read request")
35+
}
36+
return req
37+
}
38+
39+
var ts *httptest.Server // global, so can be used *inside* the server, too
40+
41+
func TestHTTP11(t *testing.T) {
42+
simpleHandler := func(w http.ResponseWriter, r *http.Request) {
43+
proto := r.Proto
44+
if proto != "HTTP/1.1" {
45+
t.Errorf("expected HTTP/1.1, got %s", proto)
46+
}
47+
sp := bytes.Split([]byte(ts.URL), []byte(":"))
48+
portval, err := strconv.Atoi(string(sp[2]))
49+
if err != nil {
50+
t.Errorf("cannot parse server port number")
51+
}
52+
tpl, err := template.New("fields").Parse(wantFields)
53+
if err != nil {
54+
t.Errorf("could not parse template")
55+
}
56+
type inputs struct{ Port int }
57+
wf, err := execTemplate(*tpl, "fields", inputs{Port: portval})
58+
verifier, err := NewHMACSHA256Verifier("key1", bytes.Repeat([]byte{0x03}, 64),
59+
NewVerifyConfig().SetVerifyCreated(false),
60+
Headers("@query"))
61+
if err != nil {
62+
t.Errorf("could not create verifier")
63+
}
64+
sigInput, err := verifyRequestDebug("sig1", *verifier, r)
65+
if err != nil {
66+
t.Errorf("failed to verify request: sig input: %s\nerr: %v", sigInput, err)
67+
}
68+
69+
if sigInput != wf {
70+
// t.Errorf("expected: %s\ngot: %s\n", wantFields, sigInput)
71+
t.Errorf("unexpected fields: %s\n", diff.CharacterDiff(sigInput, wantFields))
72+
} // TODO: copy Host from request/response to URL if empty in URL
73+
w.WriteHeader(200)
74+
}
75+
ts = httptest.NewServer(http.HandlerFunc(simpleHandler))
76+
defer ts.Close()
77+
78+
signer, err := NewHMACSHA256Signer("key1", bytes.Repeat([]byte{0x03}, 64),
79+
NewSignConfig().SignCreated(false),
80+
Headers("kuku", "@query", "@method", "@target-uri"))
81+
client := NewDefaultClient("sig1", signer, nil, nil)
82+
req := newClientRequest(t, "GET", ts.URL+"/"+"?k1=v1&k2", httpreq5)
83+
req.Header.Set("Kuku", "my awesome header")
84+
res, err := client.Do(req)
85+
if err != nil {
86+
t.Errorf("%v", err)
87+
}
88+
if res != nil {
89+
_, err = io.ReadAll(res.Body)
90+
_ = res.Body.Close()
91+
if err != nil {
92+
t.Errorf("%v", err)
93+
}
94+
95+
if res.Status != "200 OK" {
96+
t.Errorf("Bad status returned")
97+
}
98+
}
99+
}

httpparse.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,18 @@ func parseRequest(req *http.Request) (*parsedMessage, error) {
2727
if err != nil {
2828
return nil, fmt.Errorf("cannot parse query: %s", req.URL.RawQuery)
2929
}
30-
return &parsedMessage{derived: generateReqDerivedComponents(req), url: req.URL, headers: normalizeHeaderNames(req.Header), qParams: values}, nil
30+
url := req.URL
31+
if url.Host == "" {
32+
url.Host = req.Host
33+
}
34+
if url.Scheme == "" {
35+
if req.TLS == nil {
36+
url.Scheme = "http"
37+
} else {
38+
url.Scheme = "https"
39+
}
40+
}
41+
return &parsedMessage{derived: generateReqDerivedComponents(req), url: url, headers: normalizeHeaderNames(req.Header), qParams: values}, nil
3142
}
3243

3344
func normalizeHeaderNames(header http.Header) http.Header {

signatures.go

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ func generateSigParams(config *SignConfig, keyID, alg string, foreignSigner inte
171171
// SignRequest signs an HTTP request. Returns the Signature-Input and the Signature header values.
172172
//
173173
func SignRequest(signatureName string, signer Signer, req *http.Request) (signatureInputHeader, signature string, err error) {
174-
signatureInputHeader, signature, _, err = signRequestDebug(signatureName, signer, req)
174+
signatureInputHeader, signature, signatureInput, err := signRequestDebug(signatureName, signer, req)
175+
_ = signatureInput
175176
return
176177
}
177178

@@ -230,20 +231,24 @@ func addPseudoHeaders(message *parsedMessage, rr *requestResponse, fields Fields
230231

231232
//
232233
// VerifyRequest verifies a signed HTTP request. Returns an error if verification failed for any reason, otherwise nil.
233-
//
234-
func VerifyRequest(signatureName string, verifier Verifier, req *http.Request) (err error) {
234+
func VerifyRequest(signatureName string, verifier Verifier, req *http.Request) error {
235+
_, err := verifyRequestDebug(signatureName, verifier, req)
236+
return err
237+
}
238+
239+
func verifyRequestDebug(signatureName string, verifier Verifier, req *http.Request) (signatureInput string, err error) {
235240
if req == nil {
236-
return fmt.Errorf("nil request")
241+
return "", fmt.Errorf("nil request")
237242
}
238243
if signatureName == "" {
239-
return fmt.Errorf("empty signature name")
244+
return "", fmt.Errorf("empty signature name")
240245
}
241246
if verifier.config.requestResponse != nil {
242-
return fmt.Errorf("use request-response only to verify responses")
247+
return "", fmt.Errorf("use request-response only to verify responses")
243248
}
244249
parsedMessage, err := parseRequest(req)
245250
if err != nil {
246-
return err
251+
return "", err
247252
}
248253
return verifyMessage(*verifier.config, signatureName, verifier, *parsedMessage, verifier.fields)
249254
}
@@ -352,48 +357,47 @@ func VerifyResponse(signatureName string, verifier Verifier, res *http.Response)
352357
return err
353358
}
354359
extendedFields := addPseudoHeaders(parsedMessage, verifier.config.requestResponse, verifier.fields)
355-
return verifyMessage(*verifier.config, signatureName, verifier, *parsedMessage, extendedFields)
360+
_, err = verifyMessage(*verifier.config, signatureName, verifier, *parsedMessage, extendedFields)
361+
return err
356362
}
357363

358-
func verifyMessage(config VerifyConfig, name string, verifier Verifier, message parsedMessage, fields Fields) error {
364+
func verifyMessage(config VerifyConfig, name string, verifier Verifier, message parsedMessage, fields Fields) (string, error) {
359365
wsi, err := message.getDictHeader("signature-input", name)
360366
if err != nil {
361-
return fmt.Errorf("missing \"signature-input\" header, or cannot find signature \"%s\": %w", name, err)
367+
return "", fmt.Errorf("missing \"signature-input\" header, or cannot find signature \"%s\": %w", name, err)
362368
}
363369
if len(wsi) > 1 {
364-
return fmt.Errorf("multiple \"signature-header\" values for %s", name)
370+
return "", fmt.Errorf("multiple \"signature-header\" values for %s", name)
365371
}
366372
wantSignatureInput := wsi[0]
367373
ws, err := message.getDictHeader("signature", name)
368374
if err != nil {
369-
return fmt.Errorf("missing \"signature\" header")
375+
return "", fmt.Errorf("missing \"signature\" header")
370376
}
371377
if len(ws) > 1 {
372-
return fmt.Errorf("multiple \"signature\" values for %s", name)
378+
return "", fmt.Errorf("multiple \"signature\" values for %s", name)
373379
}
374380
wantSignature := ws[0]
375-
//delete(message.components, *fromDictHeader("signature-input", name))
376-
//delete(message.components, *fromDictHeader("signature", name))
377381
wantSigRaw, err := parseWantSignature(wantSignature)
378382
if err != nil {
379-
return err
383+
return "", err
380384
}
381385
psiSig, err := parseSignatureInput(wantSignatureInput, name)
382386
if err != nil {
383-
return err
387+
return "", err
384388
}
385389
if !(psiSig.fields.contains(&fields)) {
386-
return fmt.Errorf("actual signature does not cover all required fields")
390+
return "", fmt.Errorf("actual signature does not cover all required fields")
387391
}
388392
err = applyVerificationPolicy(verifier, psiSig, config)
389393
if err != nil {
390-
return err
394+
return "", err
391395
}
392396
signatureInput, err := generateSignatureInput(message, psiSig.fields, psiSig.origSigParams)
393397
if err != nil {
394-
return err
398+
return "", err
395399
}
396-
return verifySignature(verifier, signatureInput, wantSigRaw)
400+
return signatureInput, verifySignature(verifier, signatureInput, wantSigRaw)
397401
}
398402

399403
func applyVerificationPolicy(verifier Verifier, psi *psiSignature, config VerifyConfig) error {

0 commit comments

Comments
 (0)