Skip to content

Commit ebaebfc

Browse files
committed
More tests
1 parent 49fabe1 commit ebaebfc

File tree

8 files changed

+226
-14
lines changed

8 files changed

+226
-14
lines changed

client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
)
88

99
// Client represents an HTTP client that optionally signs requests and optionally verifies responses.
10-
// The Signer may be nil to avoid signing, and so forth.
10+
// The Signer may be nil to avoid signing. Similarly, if both Verifier and FetchVerifier are nil, no verification takes place.
1111
// The FetchVerifier callback allows to generate a Verifier based on the particular response.
1212
// Either Verifier or FetchVerifier may be specified, but not both.
13-
// The client embeds an http.Client, which may be http.DefaultClient or any other.
13+
// The client embeds an http.Client, which in most cases can be http.DefaultClient.
1414
type Client struct {
1515
SignatuerName string
1616
Signer *Signer

client_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,27 @@ func TestClient_Get(t *testing.T) {
102102
wantRes: "",
103103
wantErr: true,
104104
},
105+
{
106+
name: "verifier fails",
107+
fields: fields{
108+
sigName: "sig1",
109+
signer: func() *Signer {
110+
signer, _ := NewHMACSHA256Signer("key1", bytes.Repeat([]byte{1}, 64), NewSignConfig(), HeaderList([]string{"@method"}))
111+
return signer
112+
}(),
113+
verifier: nil,
114+
fetchVerifier: func(res *http.Response, req *http.Request) (sigName string, verifier *Verifier) {
115+
verifier, _ = NewHMACSHA256Verifier("key1", bytes.Repeat([]byte{2}, 64), NewVerifyConfig(), HeaderList([]string{"@method"}))
116+
return "name", verifier
117+
},
118+
Client: *http.DefaultClient,
119+
},
120+
args: args{
121+
url: "",
122+
},
123+
wantRes: "",
124+
wantErr: true,
125+
},
105126
}
106127

107128
ts := makeTestServer()

config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func defaultReqNotVerified(w http.ResponseWriter, _ *http.Request, err error) {
168168
}
169169

170170
// SetReqNotVerified defines a callback to be called when a request fails to verify. The default
171-
// callback sends a 401 status code with an error message that includes the error string.
171+
// callback sends a 401 status code with a generic error message.
172172
func (h *HandlerConfig) SetReqNotVerified(f func(w http.ResponseWriter, r *http.Request, err error)) *HandlerConfig {
173173
h.reqNotVerified = f
174174
return h

crypto_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package httpsign
22

33
import (
4+
"crypto/rand"
5+
"crypto/rsa"
46
"reflect"
57
"strings"
68
"testing"
@@ -121,3 +123,59 @@ func TestSigner_sign(t *testing.T) {
121123
})
122124
}
123125
}
126+
127+
func TestNewRSASigner(t *testing.T) {
128+
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
129+
if err != nil {
130+
t.Errorf("failed to gen private key")
131+
}
132+
_ = privateKey.Public()
133+
134+
type args struct {
135+
keyID string
136+
key *rsa.PrivateKey
137+
config *SignConfig
138+
fields Fields
139+
}
140+
tests := []struct {
141+
name string
142+
args args
143+
want *Signer
144+
wantErr bool
145+
}{
146+
{
147+
name: "empty key ID",
148+
args: args{
149+
keyID: "",
150+
key: privateKey,
151+
config: nil,
152+
fields: nil,
153+
},
154+
want: nil,
155+
wantErr: true,
156+
},
157+
{
158+
name: "nil key",
159+
args: args{
160+
keyID: "kk",
161+
key: nil,
162+
config: NewSignConfig(),
163+
fields: nil,
164+
},
165+
want: nil,
166+
wantErr: true,
167+
},
168+
}
169+
for _, tt := range tests {
170+
t.Run(tt.name, func(t *testing.T) {
171+
got, err := NewRSASigner(tt.args.keyID, tt.args.key, tt.args.config, tt.args.fields)
172+
if (err != nil) != tt.wantErr {
173+
t.Errorf("NewRSASigner() error = %v, wantErr %v", err, tt.wantErr)
174+
return
175+
}
176+
if !reflect.DeepEqual(got, tt.want) {
177+
t.Errorf("NewRSASigner() got = %v, want %v", got, tt.want)
178+
}
179+
})
180+
}
181+
}

ecdsa.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import (
1010
// These functions extend the ecdsa package by adding raw, JWS-style signatures
1111

1212
func ecdsaSignRaw(rd io.Reader, priv *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
13+
if priv == nil {
14+
return nil, fmt.Errorf("nil private key")
15+
}
1316
r, s, err := ecdsa.Sign(rd, priv, hash)
1417
if err != nil {
1518
return nil, err
@@ -31,6 +34,9 @@ func ecdsaSignRaw(rd io.Reader, priv *ecdsa.PrivateKey, hash []byte) ([]byte, er
3134
}
3235

3336
func ecdsaVerifyRaw(pub *ecdsa.PublicKey, hash []byte, sig []byte) (bool, error) {
37+
if pub == nil {
38+
return false, fmt.Errorf("nil public key")
39+
}
3440
curve := pub.Params().Name
3541
lr, ls, err := sigComponentLen(curve)
3642
if err != nil {

ecdsa_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package httpsign
2+
3+
import (
4+
"bytes"
5+
"crypto/ecdsa"
6+
"crypto/elliptic"
7+
"crypto/rand"
8+
"testing"
9+
)
10+
11+
func Test_sigComponentLen(t *testing.T) {
12+
type args struct {
13+
curve string
14+
}
15+
tests := []struct {
16+
name string
17+
args args
18+
want int
19+
want1 int
20+
wantErr bool
21+
}{
22+
{
23+
name: "bad curve",
24+
args: args{
25+
curve: "P-77",
26+
},
27+
want: 0,
28+
want1: 0,
29+
wantErr: true,
30+
},
31+
}
32+
for _, tt := range tests {
33+
t.Run(tt.name, func(t *testing.T) {
34+
got, got1, err := sigComponentLen(tt.args.curve)
35+
if (err != nil) != tt.wantErr {
36+
t.Errorf("sigComponentLen() error = %v, wantErr %v", err, tt.wantErr)
37+
return
38+
}
39+
if got != tt.want {
40+
t.Errorf("sigComponentLen() got = %v, want %v", got, tt.want)
41+
}
42+
if got1 != tt.want1 {
43+
t.Errorf("sigComponentLen() got1 = %v, want %v", got1, tt.want1)
44+
}
45+
})
46+
}
47+
}
48+
49+
func Test_ecdsaVerifyRaw(t *testing.T) {
50+
privKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
51+
if err != nil {
52+
t.Errorf("Failed to generate private key")
53+
}
54+
pubKey := privKey.Public().(*ecdsa.PublicKey)
55+
privKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
56+
if err != nil {
57+
t.Errorf("Failed to generate private key")
58+
}
59+
pubKey2 := privKey2.Public().(*ecdsa.PublicKey)
60+
type args struct {
61+
pub *ecdsa.PublicKey
62+
hash []byte
63+
sig []byte
64+
}
65+
tests := []struct {
66+
name string
67+
args args
68+
want bool
69+
wantErr bool
70+
}{
71+
{
72+
name: "nil pub",
73+
args: args{
74+
pub: nil,
75+
hash: bytes.Repeat([]byte{88}, 1024),
76+
sig: nil,
77+
},
78+
want: false,
79+
wantErr: true,
80+
},
81+
{
82+
name: "bad curve",
83+
args: args{
84+
pub: pubKey,
85+
hash: bytes.Repeat([]byte{88}, 1024),
86+
sig: nil,
87+
},
88+
want: false,
89+
wantErr: true,
90+
},
91+
{
92+
name: "bad sig",
93+
args: args{
94+
pub: pubKey2,
95+
hash: bytes.Repeat([]byte{88}, 1024),
96+
sig: nil,
97+
},
98+
want: false,
99+
wantErr: true,
100+
},
101+
}
102+
for _, tt := range tests {
103+
t.Run(tt.name, func(t *testing.T) {
104+
got, err := ecdsaVerifyRaw(tt.args.pub, tt.args.hash, tt.args.sig)
105+
if (err != nil) != tt.wantErr {
106+
t.Errorf("ecdsaVerifyRaw() error = %v, wantErr %v", err, tt.wantErr)
107+
return
108+
}
109+
if got != tt.want {
110+
t.Errorf("ecdsaVerifyRaw() got = %v, want %v", got, tt.want)
111+
}
112+
})
113+
}
114+
}

handler_test.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func Test_WrapHandler(t *testing.T) {
6060

6161
// test various failures
6262
func TestWrapHandlerServerSigns(t *testing.T) {
63-
serverSignsTestCase := func(t *testing.T, nilSigner, dontSignResponse, earlyExpires, noSigner, badKey, badAlgs bool) {
63+
serverSignsTestCase := func(t *testing.T, nilSigner, dontSignResponse, earlyExpires, noSigner, badKey, badAlgs, verifyRequest bool) {
6464
// Callback to let the server locate its signing key and configuration
6565
var signConfig *SignConfig
6666
if !earlyExpires {
@@ -100,6 +100,13 @@ func TestWrapHandlerServerSigns(t *testing.T) {
100100
if dontSignResponse {
101101
config = config.SetSignResponse(false)
102102
}
103+
if verifyRequest {
104+
serverVerifier, _ := NewHMACSHA256Verifier("key", bytes.Repeat([]byte{9}, 64), NewVerifyConfig(), *NewFields())
105+
config = config.SetFetchVerifier(func(r *http.Request) (sigName string, verifier *Verifier) {
106+
return "sig333", serverVerifier
107+
})
108+
config = config.SetVerifyRequest(true) // override
109+
}
103110
ts := httptest.NewServer(WrapHandler(http.HandlerFunc(simpleHandler), config))
104111
defer ts.Close()
105112

@@ -117,35 +124,39 @@ func TestWrapHandlerServerSigns(t *testing.T) {
117124
verifier, _ := NewHMACSHA256Verifier("key", key, verifyConfig, *NewFields())
118125

119126
client := NewDefaultClient("sig1", nil, verifier, nil)
120-
_, err := client.Get(ts.URL)
121-
if err == nil {
122-
t.Errorf("Surprise! Signature validation was successful.")
127+
res, err := client.Get(ts.URL)
128+
if err == nil && res.StatusCode == 200 {
129+
t.Errorf("Surprise! Server sent 200 OK and signature validation was successful.")
123130
}
124131
}
125132
nilSigner := func(t *testing.T) {
126-
serverSignsTestCase(t, true, false, false, false, false, false)
133+
serverSignsTestCase(t, true, false, false, false, false, false, false)
127134
}
128135
dontSignResponse := func(t *testing.T) {
129-
serverSignsTestCase(t, false, true, false, false, false, false)
136+
serverSignsTestCase(t, false, true, false, false, false, false, false)
130137
}
131138
earlyExpires := func(t *testing.T) {
132-
serverSignsTestCase(t, false, false, true, false, false, false)
139+
serverSignsTestCase(t, false, false, true, false, false, false, false)
133140
}
134141
noSigner := func(t *testing.T) {
135-
serverSignsTestCase(t, false, false, false, true, false, false)
142+
serverSignsTestCase(t, false, false, false, true, false, false, false)
136143
}
137144
badKey := func(t *testing.T) {
138-
serverSignsTestCase(t, false, false, false, false, true, false)
145+
serverSignsTestCase(t, false, false, false, false, true, false, false)
139146
}
140147
badAlgs := func(t *testing.T) {
141-
serverSignsTestCase(t, false, false, false, false, false, true)
148+
serverSignsTestCase(t, false, false, false, false, false, true, false)
149+
}
150+
failVerify := func(t *testing.T) {
151+
serverSignsTestCase(t, false, false, false, false, false, false, true)
142152
}
143153
t.Run("nil Signer", nilSigner)
144154
t.Run("don't sign response", dontSignResponse)
145155
t.Run("early expires field", earlyExpires)
146156
t.Run("bad fetch Signer", noSigner)
147157
t.Run("wrong verification key", badKey)
148158
t.Run("failed algorithm check", badAlgs)
159+
t.Run("failed request verification", failVerify)
149160
}
150161

151162
func TestWrapHandlerServerFails(t *testing.T) { // non-default verify handler

signatures.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Package httpsign signs HTTP requests and responses as defined in draft-ietf-httpbis-message-signatures.
22
// See https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-07.html.
33
//
4-
// For client-side message signing, use SignRequest, VerifyResponse etc. For server-side operation,
4+
// For client-side message signing, use the Client wrapper. Alternatively, use SignRequest, VerifyResponse directly,
5+
// but this is more complicated.
6+
// For server-side operation,
57
// WrapHandler installs a wrapper around a normal HTTP message handler.
68
package httpsign
79

0 commit comments

Comments
 (0)