Skip to content

Commit 691910b

Browse files
authored
Merge pull request #18 from krichprollsch/expireAfter
add expiresAfter to use a relative delay for expires
2 parents d8c4d2c + 9f23d36 commit 691910b

4 files changed

Lines changed: 88 additions & 36 deletions

File tree

config.go

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,29 @@ import (
1010

1111
// SignConfig contains additional configuration for the signer.
1212
type SignConfig struct {
13-
signAlg bool
14-
signCreated bool
15-
fakeCreated int64
16-
expires int64
17-
nonce string
18-
tag string
19-
keyID *string
20-
maxBodySize int64
13+
signAlg bool
14+
signCreated bool
15+
fakeCreated int64
16+
expires int64
17+
expiresAfter int64
18+
nonce string
19+
tag string
20+
keyID *string
21+
maxBodySize int64
2122
schemeFromRequest func(*http.Request) string
2223
}
2324

2425
// NewSignConfig generates a default configuration.
2526
func NewSignConfig() *SignConfig {
2627
return &SignConfig{
27-
signAlg: true,
28-
signCreated: true,
29-
fakeCreated: 0,
30-
expires: 0,
31-
nonce: "",
32-
tag: "", // we disallow an empty tag
33-
keyID: nil,
28+
signAlg: true,
29+
signCreated: true,
30+
fakeCreated: 0,
31+
expires: 0,
32+
expiresAfter: 0,
33+
nonce: "",
34+
tag: "", // we disallow an empty tag
35+
keyID: nil,
3436
}
3537
}
3638

@@ -60,6 +62,14 @@ func (c *SignConfig) SetExpires(expires int64) *SignConfig {
6062
return c
6163
}
6264

65+
// SetExpiresAfter sets the "expires" parameter to createdTime + delay (seconds).
66+
// Use this for a relative validity window instead of an absolute timestamp.
67+
// Default: 0 (do not add the parameter).
68+
func (c *SignConfig) SetExpiresAfter(delay int64) *SignConfig {
69+
c.expiresAfter = delay
70+
return c
71+
}
72+
6373
// SetNonce adds a "nonce" string parameter whose content should be unique per signed message.
6474
// Default: empty string (do not add the parameter).
6575
func (c *SignConfig) SetNonce(nonce string) *SignConfig {
@@ -99,17 +109,17 @@ func (c *SignConfig) SetSchemeFromRequest(f func(*http.Request) string) *SignCon
99109

100110
// VerifyConfig contains additional configuration for the verifier.
101111
type VerifyConfig struct {
102-
verifyCreated bool
103-
notNewerThan time.Duration
104-
notOlderThan time.Duration
105-
allowedAlgs []string
106-
rejectExpired bool
107-
keyID *string
108-
dateWithin time.Duration
109-
allowedTags []string
110-
maxBodySize int64
112+
verifyCreated bool
113+
notNewerThan time.Duration
114+
notOlderThan time.Duration
115+
allowedAlgs []string
116+
rejectExpired bool
117+
keyID *string
118+
dateWithin time.Duration
119+
allowedTags []string
120+
maxBodySize int64
111121
schemeFromRequest func(*http.Request) string
112-
nonceValidator func(string) error
122+
nonceValidator func(string) error
113123
}
114124

115125
// SetNonceValidator sets a callback to validate the nonce parameter during verification.

handler_test.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@ func Test_WrapHandler(t *testing.T) {
5959

6060
// test various failures
6161
func TestWrapHandlerServerSigns(t *testing.T) {
62-
serverSignsTestCase := func(t *testing.T, nilSigner, dontSignResponse, earlyExpires, noSigner, badKey, badAlgs, verifyRequest bool) {
62+
serverSignsTestCase := func(t *testing.T, nilSigner, dontSignResponse, earlyExpires, earlyExpiresAfter, noSigner, badKey, badAlgs, verifyRequest bool) {
6363
// Callback to let the server locate its signing key and configuration
6464
var signConfig *SignConfig
65-
if !earlyExpires {
66-
signConfig = NewSignConfig()
67-
} else {
65+
if earlyExpires {
6866
signConfig = NewSignConfig().SetExpires(2000)
67+
} else if earlyExpiresAfter {
68+
signConfig = NewSignConfig().SetExpiresAfter(1).setFakeCreated(1000)
69+
} else {
70+
signConfig = NewSignConfig()
6971
}
7072
fetchSigner := func(res http.Response, r *http.Request) (string, *Signer) {
7173
sigName := "sig1"
@@ -128,29 +130,33 @@ func TestWrapHandlerServerSigns(t *testing.T) {
128130
}
129131
}
130132
nilSigner := func(t *testing.T) {
131-
serverSignsTestCase(t, true, false, false, false, false, false, false)
133+
serverSignsTestCase(t, true, false, false, false, false, false, false, false)
132134
}
133135
dontSignResponse := func(t *testing.T) {
134-
serverSignsTestCase(t, false, true, false, false, false, false, false)
136+
serverSignsTestCase(t, false, true, false, false, false, false, false, false)
135137
}
136138
earlyExpires := func(t *testing.T) {
137-
serverSignsTestCase(t, false, false, true, false, false, false, false)
139+
serverSignsTestCase(t, false, false, true, false, false, false, false, false)
140+
}
141+
earlyExpiresAfter := func(t *testing.T) {
142+
serverSignsTestCase(t, false, false, false, true, false, false, false, false)
138143
}
139144
noSigner := func(t *testing.T) {
140-
serverSignsTestCase(t, false, false, false, true, false, false, false)
145+
serverSignsTestCase(t, false, false, false, false, true, false, false, false)
141146
}
142147
badKey := func(t *testing.T) {
143-
serverSignsTestCase(t, false, false, false, false, true, false, false)
148+
serverSignsTestCase(t, false, false, false, false, false, true, false, false)
144149
}
145150
badAlgs := func(t *testing.T) {
146-
serverSignsTestCase(t, false, false, false, false, false, true, false)
151+
serverSignsTestCase(t, false, false, false, false, false, false, true, false)
147152
}
148153
failVerify := func(t *testing.T) {
149-
serverSignsTestCase(t, false, false, false, false, false, false, true)
154+
serverSignsTestCase(t, false, false, false, false, false, false, false, true)
150155
}
151156
t.Run("nil Signer", nilSigner)
152157
t.Run("don't sign response", dontSignResponse)
153158
t.Run("early expires field", earlyExpires)
159+
t.Run("early expires after field", earlyExpiresAfter)
154160
t.Run("bad fetch Signer", noSigner)
155161
t.Run("wrong verification key", badKey)
156162
t.Run("failed algorithm check", badAlgs)

signatures.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,12 @@ func generateSigParams(config *SignConfig, alg string, foreignSigner interface{}
279279
if config.expires != 0 {
280280
p.Add("expires", config.expires)
281281
}
282+
if config.expiresAfter != 0 {
283+
if config.expires != 0 {
284+
return "", fmt.Errorf("cannot use both expires and expiresAfter")
285+
}
286+
p.Add("expires", config.expiresAfter+createdTime)
287+
}
282288
if config.nonce != "" {
283289
qNonce, err := quotedString(config.nonce)
284290
if err != nil {

signatures_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,36 @@ func TestMessageSignAndVerifyResponseHMAC(t *testing.T) {
981981
}
982982
}
983983

984+
func TestExpiresAfterCalculation(t *testing.T) {
985+
fields := Headers("@status", "date", "content-type")
986+
signatureName := "sigres"
987+
key, _ := base64.StdEncoding.DecodeString("uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ==")
988+
config := NewSignConfig().SetExpiresAfter(60).setFakeCreated(1000).SetKeyID("test-shared-secret")
989+
signer, _ := NewHMACSHA256Signer(key, config, fields)
990+
res := readResponse(httpres2)
991+
sigInput, _, err := SignResponse(signatureName, *signer, res, nil)
992+
if err != nil {
993+
t.Fatalf("SignResponse failed: %s", err)
994+
}
995+
// expires should be fakeCreated + expiresAfter = 1000 + 60 = 1060
996+
if !strings.Contains(sigInput, "expires=1060") {
997+
t.Errorf("expected expires=1060 in signature input, got: %s", sigInput)
998+
}
999+
}
1000+
1001+
func TestExpiresAndExpiresAfterConflict(t *testing.T) {
1002+
fields := Headers("@status", "date", "content-type")
1003+
signatureName := "sigres"
1004+
key, _ := base64.StdEncoding.DecodeString("uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ==")
1005+
config := NewSignConfig().SetExpires(2000).SetExpiresAfter(60).SetKeyID("test-shared-secret")
1006+
signer, _ := NewHMACSHA256Signer(key, config, fields)
1007+
res := readResponse(httpres2)
1008+
_, _, err := SignResponse(signatureName, *signer, res, nil)
1009+
if err == nil {
1010+
t.Errorf("expected error when both SetExpires and SetExpiresAfter are set")
1011+
}
1012+
}
1013+
9841014
func TestSignAndVerifyRSAPSS(t *testing.T) {
9851015
config := NewSignConfig().SignAlg(false).setFakeCreated(1618884475).SetKeyID("test-key-rsa-pss")
9861016
fields := Headers("@authority", "date", "content-type")

0 commit comments

Comments
 (0)