Pad non-padded secrets. This lets us continue building on <= go1.8.

- Add tests for secrets using various padding methods.
- Add a new method/test to append padding to non-padded secrets.
This commit is contained in:
Aaron Bieber
2018-04-18 09:16:40 -06:00
committed by Kyle Isom
parent 5fd928f69a
commit bbc82ff8de
5 changed files with 113 additions and 4 deletions

View File

@@ -88,9 +88,9 @@ func hotpFromURL(u *url.URL) (*HOTP, string, error) {
}
}
key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(secret)
key, err := base32.StdEncoding.DecodeString(Pad(secret))
if err != nil {
// secret isn't base32 encoded
// assume secret isn't base32 encoded
key = []byte(secret)
}
otp := NewHOTP(key, counter, digits)

View File

@@ -69,6 +69,46 @@ func TestURL(t *testing.T) {
}
}
// This test makes sure we can generate codes for padded and non-padded
// entries
func TestPaddedURL(t *testing.T) {
var urlList = []string{
"otpauth://hotp/?secret=ME",
"otpauth://hotp/?secret=MEFR",
"otpauth://hotp/?secret=MFRGG",
"otpauth://hotp/?secret=MFRGGZA",
"otpauth://hotp/?secret=a6mryljlbufszudtjdt42nh5by=======",
"otpauth://hotp/?secret=a6mryljlbufszudtjdt42nh5by",
"otpauth://hotp/?secret=a6mryljlbufszudtjdt42nh5by%3D%3D%3D%3D%3D%3D%3D",
}
var codeList = []string{
"413198",
"770938",
"670717",
"402378",
"069864",
"069864",
"069864",
}
for i := range urlList {
if o, id, err := FromURL(urlList[i]); err != nil {
fmt.Println("hotp: URL should have parsed successfully")
fmt.Printf("\turl was: %s\n", urlList[i])
t.FailNow()
fmt.Printf("\t%s, %s\n", o.OTP(), id)
} else {
code2 := o.OTP()
if code2 != codeList[i] {
fmt.Printf("hotp: mismatched OTPs\n")
fmt.Printf("\texpected: %s\n", codeList[i])
fmt.Printf("\t actual: %s\n", code2)
t.FailNow()
}
}
}
}
// This test attempts a variety of invalid urls against the parser
// to ensure they fail.
func TestBadURL(t *testing.T) {

View File

@@ -150,9 +150,9 @@ func totpFromURL(u *url.URL) (*TOTP, string, error) {
}
}
key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(secret)
key, err := base32.StdEncoding.DecodeString(Pad(secret))
if err != nil {
// secret isn't base32 encoded
// assume secret isn't base32 encoded
key = []byte(secret)
}
otp := NewTOTP(key, 0, period, digits, algo)

16
util.go Normal file
View File

@@ -0,0 +1,16 @@
package twofactor
import (
"strings"
)
// Pad calculates the number of '='s to add to our encoded string
// to make base32.StdEncoding.DecodeString happy
func Pad(s string) string {
if !strings.HasSuffix(s, "=") && len(s)%8 != 0 {
for len(s)%8 != 0 {
s += "="
}
}
return s
}

53
util_test.go Normal file
View File

@@ -0,0 +1,53 @@
package twofactor
import (
"encoding/base32"
"fmt"
"math/rand"
"strings"
"testing"
)
const letters = "1234567890!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randString() string {
b := make([]byte, rand.Intn(len(letters)))
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return base32.StdEncoding.EncodeToString(b)
}
func TestPadding(t *testing.T) {
for i := 0; i < 300; i++ {
b := randString()
origEncoding := string(b)
modEncoding := strings.Replace(string(b), "=", "", -1)
str, err := base32.StdEncoding.DecodeString(origEncoding)
if err != nil {
fmt.Println("Can't decode: ", string(b))
t.FailNow()
}
paddedEncoding := Pad(modEncoding)
if origEncoding != paddedEncoding {
fmt.Println("Padding failed:")
fmt.Printf("Expected: '%s'", origEncoding)
fmt.Printf("Got: '%s'", paddedEncoding)
t.FailNow()
} else {
mstr, err := base32.StdEncoding.DecodeString(paddedEncoding)
if err != nil {
fmt.Println("Can't decode: ", paddedEncoding)
t.FailNow()
}
if string(mstr) != string(str) {
fmt.Println("Re-padding failed:")
fmt.Printf("Expected: '%s'", str)
fmt.Printf("Got: '%s'", mstr)
t.FailNow()
}
}
}
}