Skip to content

Commit

Permalink
Use single source of truth for equality checks (#1)
Browse files Browse the repository at this point in the history
Enforced a single point of token equality check for all functions. Added a test case to demonstrate why the verifyToken can not just be used.
  • Loading branch information
zepatrik authored Aug 27, 2020
1 parent ee7691f commit 0bc5e56
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
14 changes: 8 additions & 6 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ func VerifyToken(realToken, sentToken string) bool {
if len(s) == 2*tokenLength {
s = unmaskToken(s)
}
return subtle.ConstantTimeCompare(r, s) == 1 && len(r) > 0 && len(s) > 0
return tokensEqual(r, s)
}

// verifyToken expects the realToken to be unmasked and the sentToken to be masked
func verifyToken(realToken, sentToken []byte) bool {
realN := len(realToken)
sentN := len(sentToken)
Expand All @@ -83,15 +84,16 @@ func verifyToken(realToken, sentToken []byte) bool {
// sentN == 2*tokenLength means the token is masked.

if realN == tokenLength && sentN == 2*tokenLength {
return verifyMasked(realToken, sentToken)
return tokensEqual(realToken, unmaskToken(sentToken))
}
return false
}

// Verifies the masked token
func verifyMasked(realToken, sentToken []byte) bool {
sentPlain := unmaskToken(sentToken)
return subtle.ConstantTimeCompare(realToken, sentPlain) == 1
// tokensEqual expects both tokens to be unmasked
func tokensEqual(realToken, sentToken []byte) bool {
return len(realToken) == tokenLength &&
len(sentToken) == tokenLength &&
subtle.ConstantTimeCompare(realToken, sentToken) == 1
}

func checkForPRNG() {
Expand Down
45 changes: 45 additions & 0 deletions token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nosurf

import (
"crypto/rand"
"encoding/base64"
"testing"
)

Expand Down Expand Up @@ -83,3 +84,47 @@ func TestVerifyTokenBase64Invalid(t *testing.T) {
}
}
}

func TestVerifyTokenUnMasked(t *testing.T) {
for i, tc := range []struct {
real string
send string
valid bool
}{
{
real: "qwertyuiopasdfghjklzxcvbnm123456",
send: "qwertyuiopasdfghjklzxcvbnm123456",
valid: true,
},
{
real: "qwertyuiopasdfghjklzxcvbnm123456",
send: "qwertyuiopasdfghjklzxcvbnm123456" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
valid: true,
},
{
real: "qwertyuiopasdfghjklzxcvbnm123456" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
send: "qwertyuiopasdfghjklzxcvbnm123456" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
valid: true,
},
{
real: "qwertyuiopasdfghjklzxcvbnm123456" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
send: "qwertyuiopasdfghjklzxcvbnm123456",
valid: true,
},
} {
if VerifyToken(
base64.StdEncoding.EncodeToString([]byte(tc.real)),
base64.StdEncoding.EncodeToString([]byte(tc.send)),
) != tc.valid {
t.Errorf("Verify token returned wrong result for case %d: %+v", i, tc)
}
}
}

0 comments on commit 0bc5e56

Please sign in to comment.