Skip to content

Commit

Permalink
Pad non-padded secrets. This lets us continue building on <= go1.8.
Browse files Browse the repository at this point in the history
- Add tests for secrets using various padding methods.
- Add a new method/test to append padding to non-padded secrets.
  • Loading branch information
qbit authored and kisom committed Apr 18, 2018
1 parent 5fd928f commit bbc82ff
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 4 deletions.
4 changes: 2 additions & 2 deletions hotp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
40 changes: 40 additions & 0 deletions otp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions totp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions util_test.go
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
}

0 comments on commit bbc82ff

Please sign in to comment.