136 lines
3.7 KiB
Go
136 lines
3.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestMintParseRoundTrip(t *testing.T) {
|
|
seen := map[string]bool{}
|
|
for i := 0; i < 100; i++ {
|
|
token, keyID, hash, err := Mint()
|
|
if err != nil {
|
|
t.Fatalf("Mint: %v", err)
|
|
}
|
|
if seen[keyID] {
|
|
t.Fatalf("Mint reused key id %q", keyID)
|
|
}
|
|
seen[keyID] = true
|
|
|
|
if !strings.HasPrefix(token, Prefix+"_") {
|
|
t.Errorf("token %q lacks the %q prefix", token, Prefix)
|
|
}
|
|
gotID, secret, err := Parse(token)
|
|
if err != nil {
|
|
t.Fatalf("Parse(%q): %v", token, err)
|
|
}
|
|
if gotID != keyID {
|
|
t.Errorf("Parse key id = %q, want %q", gotID, keyID)
|
|
}
|
|
if len(gotID) != KeyIDLen {
|
|
t.Errorf("key id length = %d, want %d", len(gotID), KeyIDLen)
|
|
}
|
|
if !SecretMatches(secret, hash[:]) {
|
|
t.Error("minted secret does not match its own hash")
|
|
}
|
|
if strings.Contains(token, keyID+"_"+keyID) {
|
|
t.Error("secret must not repeat the key id")
|
|
}
|
|
if !ValidKeyID(keyID) {
|
|
t.Errorf("ValidKeyID rejected a minted id %q", keyID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseRejectsMalformed(t *testing.T) {
|
|
good, keyID, _, err := Mint()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, secret, err := Parse(good)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
token string
|
|
}{
|
|
{"empty", ""},
|
|
{"no prefix", keyID + "_" + secret},
|
|
{"wrong prefix", "xyz_" + keyID + "_" + secret},
|
|
{"prefix only", "pgs_"},
|
|
{"no separator", "pgs_" + keyID + secret},
|
|
{"short key id", "pgs_" + keyID[:15] + "_" + secret},
|
|
{"long key id", "pgs_" + keyID + "a_" + secret},
|
|
{"short secret", "pgs_" + keyID + "_" + secret[:42]},
|
|
{"long secret", "pgs_" + keyID + "_" + secret + "a"},
|
|
{"uppercase key id", "pgs_" + strings.ToUpper(keyID) + "_" + secret},
|
|
{"key id with 0 (not in base32)", "pgs_0" + keyID[1:] + "_" + secret},
|
|
{"key id with 1 (not in base32)", "pgs_1" + keyID[1:] + "_" + secret},
|
|
{"secret with padding", "pgs_" + keyID + "_" + secret[:42] + "="},
|
|
{"secret with slash", "pgs_" + keyID + "_" + secret[:42] + "/"},
|
|
{"secret with plus", "pgs_" + keyID + "_" + secret[:42] + "+"},
|
|
{"embedded NUL", "pgs_" + keyID + "_" + secret[:42] + "\x00"},
|
|
{"leading space", " " + good},
|
|
{"newline", good + "\n"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if _, _, err := Parse(tc.token); err == nil {
|
|
t.Errorf("Parse(%q) accepted a malformed token", tc.token)
|
|
}
|
|
})
|
|
}
|
|
|
|
// The parse error must never quote the input: these errors reach logs.
|
|
if _, _, err := Parse(good[:len(good)-1] + "x"); err != nil {
|
|
if strings.Contains(err.Error(), secret[:20]) {
|
|
t.Error("parse error leaks part of the presented secret")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecretMatches(t *testing.T) {
|
|
hash := HashSecret("correct horse battery staple")
|
|
if !SecretMatches("correct horse battery staple", hash[:]) {
|
|
t.Error("matching secret rejected")
|
|
}
|
|
if SecretMatches("correct horse battery stapl", hash[:]) {
|
|
t.Error("truncated secret accepted")
|
|
}
|
|
if SecretMatches("", hash[:]) {
|
|
t.Error("empty secret accepted")
|
|
}
|
|
if SecretMatches("correct horse battery staple", nil) {
|
|
t.Error("nil stored hash accepted")
|
|
}
|
|
if SecretMatches("correct horse battery staple", hash[:16]) {
|
|
t.Error("truncated stored hash accepted")
|
|
}
|
|
}
|
|
|
|
func TestValidKeyID(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
want bool
|
|
}{
|
|
{"abcdefghijklmnop", true},
|
|
{"234567234567abcd", true},
|
|
{"", false},
|
|
{"abcdefghijklmno", false}, // 15
|
|
{"abcdefghijklmnopq", false}, // 17
|
|
{"ABCDEFGHIJKLMNOP", false},
|
|
{"abcdefghijklmno0", false},
|
|
{"abcdefghijklmno1", false},
|
|
{"abcdefghijklmno8", false},
|
|
{"abcdefghijklmno-", false},
|
|
{"abcdefghijklmn/p", false},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := ValidKeyID(tc.in); got != tc.want {
|
|
t.Errorf("ValidKeyID(%q) = %v, want %v", tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|