Skip to content

Commit 06e309a

Browse files
committed
Refactored, most tests are passing
1 parent d300d07 commit 06e309a

File tree

4 files changed

+124
-113
lines changed

4 files changed

+124
-113
lines changed

fields.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (f *field) String() string {
2525
// HeaderList is a simple way to generate a Fields list, where only simple header names and specialty headers
2626
// are needed.
2727
func HeaderList(hs []string) Fields {
28-
f := []field{}
28+
var f []field
2929
for _, h := range hs {
3030
hd := strings.ToLower(h) // loop variable scope pitfall!
3131
f = append(f, field{hd, "", ""})
@@ -85,8 +85,9 @@ func fromDictHeader(hdr, key string) *field {
8585

8686
// AddDictHeader indicates that out of a header structured as a dictionary, a specific key value is signed/verified
8787
func (fs *Fields) AddDictHeader(hdr, key string) *Fields {
88-
k := strings.ToLower(key)
89-
return fs.addHeaderAndFlag(hdr, "key", k)
88+
f := fromDictHeader(hdr, key)
89+
*fs = append(*fs, *f)
90+
return fs
9091
}
9192

9293
func (f field) asSignatureInput() (string, error) {

httpparse.go

Lines changed: 19 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,49 @@ package httpsign
22

33
import (
44
"fmt"
5-
"github.com/dunglas/httpsfv"
65
"net/http"
76
"net/url"
87
"strconv"
98
"strings"
109
)
1110

1211
// some fields (specifically, query params) may appear more than once, and those occurrences are ordered.
13-
type components map[field][]string
12+
type components map[string]string
1413

1514
type parsedMessage struct {
16-
components components
17-
}
18-
19-
type fvPair struct {
20-
f field
21-
v []string
22-
}
23-
24-
func matchFields(comps components, fields Fields) ([]fvPair, error) {
25-
// Components for signature are ordered, thus an array of pairs and not a map
26-
matched := make([]fvPair, 0)
27-
for _, f := range fields {
28-
if v, found := comps[f]; found {
29-
matched = append(matched, fvPair{f, v})
30-
} else {
31-
return nil, fmt.Errorf("missing component \"%s\"", f.name)
32-
}
33-
}
34-
return matched, nil
15+
derived components
16+
url *url.URL
17+
headers http.Header
18+
qParams url.Values
3519
}
3620

3721
func parseRequest(req *http.Request) (*parsedMessage, error) {
3822
err := validateMessageHeaders(req.Header)
3923
if err != nil {
4024
return nil, err
4125
}
42-
components := components{}
43-
generateReqSpecialtyComponents(req, components)
44-
err = generateHeaderComponents(req.Header, components)
45-
if err != nil {
46-
return nil, err
47-
}
4826
values, err := url.ParseQuery(req.URL.RawQuery)
4927
if err != nil {
5028
return nil, fmt.Errorf("cannot parse query: %s", req.URL.RawQuery)
5129
}
52-
generateQueryParams(values, components)
53-
54-
return &parsedMessage{components}, nil
30+
return &parsedMessage{derived: generateReqDerivedComponents(req), url: req.URL, headers: normalizeHeaderNames(req.Header), qParams: values}, nil
5531
}
5632

57-
func generateQueryParams(v map[string][]string, components components) {
58-
for name, values := range v {
59-
components[*fromQueryParam(name)] = values
33+
func normalizeHeaderNames(header http.Header) http.Header {
34+
var t http.Header = http.Header{}
35+
for k, v := range header {
36+
t[strings.ToLower(k)] = v
6037
}
38+
return t
6139
}
6240

6341
func parseResponse(res *http.Response) (*parsedMessage, error) {
6442
err := validateMessageHeaders(res.Header)
6543
if err != nil {
6644
return nil, err
6745
}
68-
components := components{}
69-
generateResSpecialtyComponents(res, components)
70-
err = generateHeaderComponents(res.Header, components)
71-
if err != nil {
72-
return nil, err
73-
}
7446

75-
return &parsedMessage{components}, nil
47+
return &parsedMessage{derived: generateResDerivedComponents(res), url: nil, headers: normalizeHeaderNames(res.Header)}, nil
7648
}
7749

7850
func validateMessageHeaders(header http.Header) error {
@@ -85,37 +57,6 @@ func validateMessageHeaders(header http.Header) error {
8557
return nil
8658
}
8759

88-
func generateHeaderComponents(headers http.Header, components components) error {
89-
for hdrName, val := range headers {
90-
lower := strings.ToLower(hdrName)
91-
dict, err := httpsfv.UnmarshalDictionary(val)
92-
if err == nil { // dictionary
93-
for _, name := range dict.Names() {
94-
v, _ := dict.Get(name)
95-
switch v.(type) {
96-
case httpsfv.Item:
97-
vv, err := httpsfv.Marshal(v.(httpsfv.Item))
98-
if err != nil {
99-
return fmt.Errorf("malformed dictionry member %s: %v", name, err)
100-
}
101-
components[*fromDictHeader(lower, name)] = []string{vv}
102-
case httpsfv.InnerList:
103-
vv, err := httpsfv.Marshal(v.(httpsfv.InnerList))
104-
if err != nil {
105-
return fmt.Errorf("malformed dictionry member %s: %v", name, err)
106-
}
107-
components[*fromDictHeader(lower, name)] = []string{vv}
108-
default:
109-
return fmt.Errorf("unexpected dictionary value")
110-
}
111-
}
112-
} else {
113-
components[*fromHeaderName(lower)] = []string{foldFields(val)}
114-
}
115-
}
116-
return nil
117-
}
118-
11960
func foldFields(fields []string) string {
12061
ff := strings.TrimSpace(fields[0])
12162
for i := 1; i < len(fields); i++ {
@@ -125,10 +66,11 @@ func foldFields(fields []string) string {
12566
}
12667

12768
func specialtyComponent(name, v string, components components) {
128-
components[*fromHeaderName(name)] = []string{v}
69+
components[name] = v
12970
}
13071

131-
func generateReqSpecialtyComponents(req *http.Request, components components) {
72+
func generateReqDerivedComponents(req *http.Request) components {
73+
components := components{}
13274
specialtyComponent("@method", scMethod(req), components)
13375
theURL := req.URL
13476
specialtyComponent("@target-uri", scTargetURI(theURL), components)
@@ -138,6 +80,7 @@ func generateReqSpecialtyComponents(req *http.Request, components components) {
13880
specialtyComponent("@request-target", scRequestTarget(theURL), components)
13981
specialtyComponent("@query", scQuery(theURL), components)
14082
// @request-response does not belong here
83+
return components
14184
}
14285

14386
func scPath(theURL *url.URL) string {
@@ -171,8 +114,10 @@ func scMethod(req *http.Request) string {
171114
return req.Method
172115
}
173116

174-
func generateResSpecialtyComponents(res *http.Response, components components) {
117+
func generateResDerivedComponents(res *http.Response) components {
118+
components := components{}
175119
specialtyComponent("@status", scStatus(res), components)
120+
return components
176121
}
177122

178123
func scStatus(res *http.Response) string {

signatures.go

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import (
1111
"encoding/base64"
1212
"fmt"
1313
"github.com/dunglas/httpsfv"
14+
"log"
1415
"net/http"
16+
"strings"
1517
"time"
1618
)
1719

@@ -46,25 +48,80 @@ func encodeBytes(raw []byte) string {
4648
}
4749

4850
func generateSignatureInput(message parsedMessage, fields Fields, params string) (string, error) {
49-
mf, err := matchFields(message.components, fields)
50-
if err != nil {
51-
return "", err
52-
}
5351
inp := ""
54-
for _, c := range mf {
55-
f, err := c.f.asSignatureInput()
52+
for _, c := range fields {
53+
f, err := c.asSignatureInput()
54+
if err != nil {
55+
return "", fmt.Errorf("could not marshal %v", f)
56+
}
57+
fieldValues, err := generateFieldValues(c, message)
5658
if err != nil {
57-
return "", fmt.Errorf("could not marshal %v", c.f)
59+
return "", err
5860
}
59-
for _, v := range c.v {
61+
for _, v := range fieldValues {
6062
inp += fmt.Sprintf("%s: %s\n", f, v)
6163
}
6264
}
6365
inp += fmt.Sprintf("\"%s\": %s", "@signature-params", params)
64-
// log.Println("inp:", "\n"+inp)
66+
log.Println("inp:", "\n"+inp) // TODO!
6567
return inp, nil
6668
}
6769

70+
func generateFieldValues(f field, message parsedMessage) ([]string, error) {
71+
if f.flagName == "" {
72+
if strings.HasPrefix(f.name, "@") { // derived component
73+
return []string{message.derived[f.name]}, nil
74+
}
75+
vv, found := message.headers[f.name] // normal header, cannot use "Values" on lowercased header name
76+
if !found {
77+
return nil, fmt.Errorf("header %s not found", f.name)
78+
}
79+
return []string{foldFields(vv)}, nil
80+
}
81+
if f.name == "@query-params" && f.flagName == "name" {
82+
vals, found := message.qParams[f.flagValue]
83+
if !found {
84+
return nil, fmt.Errorf("query parameter %s not found", f.flagValue)
85+
}
86+
return vals, nil
87+
}
88+
if f.flagName == "key" { // dictionary header
89+
return message.getDictHeader(f.name, f.flagValue)
90+
}
91+
return nil, fmt.Errorf("unrecognized field %s", f)
92+
}
93+
94+
func (message *parsedMessage) getDictHeader(hdr, member string) ([]string, error) {
95+
vals, found := message.headers[hdr]
96+
if !found {
97+
return nil, fmt.Errorf("dictionary header %s not found", hdr)
98+
}
99+
dict, err := httpsfv.UnmarshalDictionary(vals)
100+
if err != nil {
101+
return nil, fmt.Errorf("cannot parse dictionary for %s", hdr)
102+
}
103+
v, found := dict.Get(member)
104+
if !found {
105+
return nil, fmt.Errorf("cannot find member %s of dictionary %s", member, hdr)
106+
}
107+
switch v.(type) {
108+
case httpsfv.Item:
109+
vv, err := httpsfv.Marshal(v.(httpsfv.Item))
110+
if err != nil {
111+
return nil, fmt.Errorf("malformed dictionry member %s: %v", hdr, err)
112+
}
113+
return []string{vv}, nil
114+
case httpsfv.InnerList:
115+
vv, err := httpsfv.Marshal(v.(httpsfv.InnerList))
116+
if err != nil {
117+
return nil, fmt.Errorf("malformed dictionry member %s: %v", hdr, err)
118+
}
119+
return []string{vv}, nil
120+
default:
121+
return nil, fmt.Errorf("unexpected dictionary value")
122+
}
123+
}
124+
68125
func generateSigParams(config *SignConfig, keyID, alg string, foreignSigner interface{}, fields Fields) (string, error) {
69126
p := httpsfv.NewParams()
70127
var createdTime int64
@@ -126,23 +183,23 @@ func SignResponse(signatureName string, signer Signer, res *http.Response) (sign
126183
if err != nil {
127184
return "", "", err
128185
}
129-
extendedFields := addPseudoHeaders(parsedMessage, signer.config.requestResponse, signer.fields)
130-
return signMessage(*signer.config, signatureName, signer, *parsedMessage, extendedFields)
186+
// extendedFields := addPseudoHeaders(parsedMessage, signer.config.requestResponse, signer.fields)
187+
return signMessage(*signer.config, signatureName, signer, *parsedMessage, signer.fields)
131188
}
132189

133190
// Handle the special header-like @request-response
134-
func addPseudoHeaders(message *parsedMessage, rr *requestResponse, fields Fields) Fields {
135-
if rr != nil {
136-
rrfield := field{
137-
name: "@request-response",
138-
flagName: "key",
139-
flagValue: rr.name,
140-
}
141-
message.components[rrfield] = []string{rr.signature}
142-
return append(fields, rrfield)
143-
}
144-
return fields
145-
}
191+
//func addPseudoHeaders(message *parsedMessage, rr *requestResponse, fields Fields) Fields {
192+
// if rr != nil {
193+
// rrfield := field{
194+
// name: "@request-response",
195+
// flagName: "key",
196+
// flagValue: rr.name,
197+
// }
198+
// message.components[rrfield] = []string{rr.signature}
199+
// return append(fields, rrfield)
200+
// }
201+
// return fields
202+
//}
146203

147204
//
148205
// VerifyRequest verifies a signed HTTP request. Returns an error if verification failed for any reason, otherwise nil.
@@ -207,8 +264,8 @@ func GetRequestSignature(req *http.Request, signatureName string) (string, error
207264
if err != nil {
208265
return "", err
209266
}
210-
ws, found := parsedMessage.components[*fromDictHeader("signature", signatureName)]
211-
if !found {
267+
ws, err := parsedMessage.getDictHeader("signature", signatureName)
268+
if err != nil {
212269
return "", fmt.Errorf("missing \"signature\" header for \"%s\"", signatureName)
213270
}
214271
if len(ws) > 1 {
@@ -223,8 +280,8 @@ func GetRequestSignature(req *http.Request, signatureName string) (string, error
223280
}
224281

225282
func messageKeyID(signatureName string, parsedMessage parsedMessage) (keyID, alg string, err error) {
226-
si, found := parsedMessage.components[*fromDictHeader("signature-input", signatureName)]
227-
if !found {
283+
si, err := parsedMessage.getDictHeader("signature-input", signatureName)
284+
if err != nil {
228285
return "", "", fmt.Errorf("missing \"signature-input\" header, or cannot find \"%s\"", signatureName)
229286
}
230287
if len(si) > 1 {
@@ -267,29 +324,29 @@ func VerifyResponse(signatureName string, verifier Verifier, res *http.Response)
267324
if err != nil {
268325
return err
269326
}
270-
extendedFields := addPseudoHeaders(parsedMessage, verifier.config.requestResponse, verifier.fields)
271-
return verifyMessage(*verifier.config, signatureName, verifier, *parsedMessage, extendedFields)
327+
// extendedFields := addPseudoHeaders(parsedMessage, verifier.config.requestResponse, verifier.fields)
328+
return verifyMessage(*verifier.config, signatureName, verifier, *parsedMessage, verifier.fields)
272329
}
273330

274331
func verifyMessage(config VerifyConfig, name string, verifier Verifier, message parsedMessage, fields Fields) error {
275-
wsi, found := message.components[*fromDictHeader("signature-input", name)]
276-
if !found {
332+
wsi, err := message.getDictHeader("signature-input", name)
333+
if err != nil {
277334
return fmt.Errorf("missing \"signature-input\" header, or cannot find signature \"%s\"", name)
278335
}
279336
if len(wsi) > 1 {
280337
return fmt.Errorf("multiple \"signature-header\" values for %s", name)
281338
}
282339
wantSignatureInput := wsi[0]
283-
ws, found := message.components[*fromDictHeader("signature", name)]
284-
if !found {
340+
ws, err := message.getDictHeader("signature", name)
341+
if err != nil {
285342
return fmt.Errorf("missing \"signature\" header")
286343
}
287344
if len(ws) > 1 {
288345
return fmt.Errorf("multiple \"signature\" values for %s", name)
289346
}
290347
wantSignature := ws[0]
291-
delete(message.components, *fromDictHeader("signature-input", name))
292-
delete(message.components, *fromDictHeader("signature", name))
348+
//delete(message.components, *fromDictHeader("signature-input", name))
349+
//delete(message.components, *fromDictHeader("signature", name))
293350
wantSigRaw, err := parseWantSignature(wantSignature)
294351
if err != nil {
295352
return err

signatures_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,15 @@ func TestRequestResponse(t *testing.T) {
10521052
res.Header.Add("Signature", sig2)
10531053

10541054
// Client verifies response
1055-
verifier2, err := NewP256Verifier("key10", pub2, NewVerifyConfig().SetRequestResponse("sig1", sig1Value).SetVerifyCreated(false), HeaderList([]string{"@status"}))
1055+
verifier2, err := NewP256Verifier("key10", pub2, NewVerifyConfig().SetRequestResponse("sigxx", sig1Value).SetVerifyCreated(false), HeaderList([]string{"@status"}))
1056+
if err != nil {
1057+
t.Errorf("Could not create second verifier: %v", err)
1058+
}
1059+
err = VerifyResponse("sig2", *verifier2, res)
1060+
if err == nil {
1061+
t.Errorf("Verification should have failed, wrong sig name in SetRequestResponse")
1062+
}
1063+
verifier2, err = NewP256Verifier("key10", pub2, NewVerifyConfig().SetRequestResponse("sig1", sig1Value).SetVerifyCreated(false), HeaderList([]string{"@status"}))
10561064
if err != nil {
10571065
t.Errorf("Could not create second verifier: %v", err)
10581066
}

0 commit comments

Comments
 (0)