-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathverify_test.go
More file actions
228 lines (208 loc) · 6.46 KB
/
verify_test.go
File metadata and controls
228 lines (208 loc) · 6.46 KB
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package httpsignature_test
import (
"bytes"
"crypto/ed25519"
"io"
"net/http"
"net/url"
"testing"
"time"
"github.com/cordialsys/offchain/pkg/httpsignature"
"github.com/cordialsys/offchain/pkg/httpsignature/signer"
"github.com/cordialsys/offchain/pkg/httpsignature/verifier"
"github.com/stretchr/testify/require"
)
type mockVerifier struct {
shouldVerify bool
}
func (m *mockVerifier) Verify(message []byte, signature []byte) bool {
return m.shouldVerify
}
func TestVerify(t *testing.T) {
// Create a fixed time for consistent tests
originalNow := httpsignature.Now
httpsignature.Now = func() time.Time {
return time.Unix(1234567890, 0)
}
defer func() {
httpsignature.Now = originalNow
}()
// Create a test key pair
pubKey, privKey, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
// Create a signer with the private key
testSigner := &signer.Ed25519Signer{
Key: privKey,
}
// Create a verifier with the public key
testVerifier, err := verifier.NewEd25519Verifier(pubKey)
require.NoError(t, err)
tests := []struct {
name string
setupReq func() *http.Request
verifier verifier.VerifierI
errorMsg string
requiredHeaders []string
}{
{
name: "valid signature",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path?query=value", bytes.NewReader(body))
err := httpsignature.Sign(req, testSigner)
require.NoError(t, err)
return req
},
verifier: testVerifier,
},
{
name: "missing content-digest header",
setupReq: func() *http.Request {
req, _ := http.NewRequest("GET", "https://example.com/path", nil)
// Don't sign the request, so headers will be missing
return req
},
verifier: testVerifier,
errorMsg: "missing content-digest header",
},
{
name: "missing signature header",
setupReq: func() *http.Request {
req, _ := http.NewRequest("GET", "https://example.com/path", nil)
// Add only content-digest header
digest := httpsignature.NewContentDigest([]byte("test"))
h := digest.Header()
req.Header.Set(h[0], h[1])
return req
},
verifier: testVerifier,
errorMsg: "missing signature header",
},
{
name: "missing signature-input header",
setupReq: func() *http.Request {
req, _ := http.NewRequest("GET", "https://example.com/path", nil)
// Add content-digest and signature headers but not signature-input
digest := httpsignature.NewContentDigest([]byte("test"))
h1 := digest.Header()
req.Header.Set(h1[0], h1[1])
sig := httpsignature.NewSignature([]byte("fake-signature"))
h2 := sig.Header()
req.Header.Set(h2[0], h2[1])
return req
},
verifier: testVerifier,
errorMsg: "missing signature-input header",
},
{
name: "invalid content-digest format",
setupReq: func() *http.Request {
req, _ := http.NewRequest("GET", "https://example.com/path", nil)
req.Header.Set(httpsignature.HeaderContentDigest, "invalid-format")
req.Header.Set(httpsignature.HeaderSignature, "iam=:c2lnbmF0dXJl:")
req.Header.Set(httpsignature.HeaderSignatureInput, "iam=(@method @path @query content-digest);created=\"1234567890\"")
return req
},
verifier: testVerifier,
errorMsg: "invalid content-digest header",
},
{
name: "content-digest mismatch",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path", bytes.NewReader(body))
// Sign the request
err := httpsignature.Sign(req, testSigner)
require.NoError(t, err)
// Replace the body with different content
req.Body = io.NopCloser(bytes.NewReader([]byte("different body")))
return req
},
verifier: testVerifier,
errorMsg: "content-digest mismatch",
},
{
name: "invalid signature",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path", bytes.NewReader(body))
// Sign the request
err := httpsignature.Sign(req, testSigner)
require.NoError(t, err)
return req
},
verifier: &mockVerifier{shouldVerify: false},
errorMsg: "signature invalid",
},
{
name: "with normalized path and query",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path", bytes.NewReader(body))
// Manually set URL without leading slashes to test normalization
req.URL = &url.URL{
Path: "path",
RawQuery: "query=value",
}
err := httpsignature.Sign(req, testSigner)
require.NoError(t, err)
return req
},
verifier: testVerifier,
},
{
name: "valid signature with additional header",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path?query=value", bytes.NewReader(body))
req.Header.Set("sub-account", "test-sub-account")
err := httpsignature.Sign(req, testSigner, "sub-account")
require.NoError(t, err)
return req
},
requiredHeaders: []string{"sub-account"},
verifier: testVerifier,
},
{
name: "valid signature with additional header missing",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path?query=value", bytes.NewReader(body))
req.Header.Set("sub-account", "test-sub-account")
err := httpsignature.Sign(req, testSigner, "sub-account")
require.NoError(t, err)
// should not verify without this
req.Header.Del("sub-account")
return req
},
verifier: testVerifier,
requiredHeaders: []string{"sub-account"},
errorMsg: "signature invalid",
},
{
name: "valid signature but missing required header",
setupReq: func() *http.Request {
body := []byte("test body")
req, _ := http.NewRequest("GET", "https://example.com/path?query=value", bytes.NewReader(body))
err := httpsignature.Sign(req, testSigner)
require.NoError(t, err)
return req
},
verifier: testVerifier,
requiredHeaders: []string{"sub-account"},
errorMsg: "missing required header in signature input: sub-account",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := tt.setupReq()
_, err := httpsignature.Verify(req, tt.verifier, tt.requiredHeaders...)
if tt.errorMsg != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errorMsg)
} else {
require.NoError(t, err)
}
})
}
}