-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmessage_digester.go
135 lines (104 loc) · 2.4 KB
/
message_digester.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package httpsig
import (
"crypto"
"crypto/subtle"
"fmt"
"io"
"net/http"
"github.com/dunglas/httpsfv"
)
type contentDigester struct {
// alg and algName are used to create message digest
alg crypto.Hash
algName DigestAlgorithm
// fromRequest is used only to verify message digest
fromRequest bool
}
func (c contentDigester) update(msg *Message) error {
if val := msg.Header.Get(headerContentDigest); len(val) != 0 {
// header already present. skipping
return nil
}
body, err := c.readBody(msg.Body)
if err != nil {
return err
}
dict := httpsfv.NewDictionary()
var algs map[DigestAlgorithm]crypto.Hash
if len(c.algName) != 0 {
algs = map[DigestAlgorithm]crypto.Hash{c.algName: c.alg}
} else {
algs = supportedAlgs
}
for name, alg := range algs {
md := alg.New()
_, _ = md.Write(body)
dict.Add(string(name), httpsfv.NewItem(md.Sum(nil)))
}
marshalled, err := httpsfv.Marshal(dict)
if err != nil {
return err
}
msg.Header.Set(headerContentDigest, marshalled)
return nil
}
func (c contentDigester) verify(msg *Message) error {
var (
hdr http.Header
getBody func() (io.ReadCloser, error)
)
if c.fromRequest {
hdr = msg.RequestHeader
getBody = msg.RequestBody
} else {
hdr = msg.Header
getBody = msg.Body
}
dict, err := httpsfv.UnmarshalDictionary(hdr.Values(headerContentDigest))
if err != nil {
return fmt.Errorf("%w: %w", ErrMalformedData, err)
}
body, err := c.readBody(getBody)
if err != nil {
return err
}
var hasVerified bool
for _, algName := range dict.Names() {
alg, known := supportedAlgs[DigestAlgorithm(algName)]
if !known {
continue
}
item, _ := dict.Get(algName)
hasVerified = true
md := alg.New()
md.Write(body)
res := md.Sum(nil)
valueItem, ok := item.(httpsfv.Item)
if !ok {
return fmt.Errorf("%w: invalid content-digest value", ErrMalformedData)
}
value, ok := valueItem.Value.([]byte)
if !ok {
return fmt.Errorf("%w: invalid content-digest value", ErrMalformedData)
}
if subtle.ConstantTimeCompare(res, value) != 1 {
return ErrContentDigestMismatch
}
}
if !hasVerified {
return ErrNoApplicableDigestFound
}
return nil
}
func (c contentDigester) readBody(getBody func() (io.ReadCloser, error)) ([]byte, error) {
body, err := getBody()
if err != nil {
return nil, err
}
data, err := io.ReadAll(body)
if err != nil {
return nil, err
}
_ = body.Close()
return data, nil
}