diff --git a/gauth.go b/gauth.go index 9df98dc..59df65f 100644 --- a/gauth.go +++ b/gauth.go @@ -73,40 +73,17 @@ func main() { } currentTS, progress := gauth.IndexNow() - prevTS := currentTS - 1 - nextTS := currentTS + 1 tw := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0) fmt.Fprintln(tw, "\tprev\tcurr\tnext") for _, record := range cfg { - name := record[0] - secret := normalizeSecret(record[1]) - prevToken := authCodeOrDie(secret, prevTS) - currentToken := authCodeOrDie(secret, currentTS) - nextToken := authCodeOrDie(secret, nextTS) - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", name, prevToken, currentToken, nextToken) + name, secret := record[0], record[1] + prev, curr, next, err := gauth.Codes(secret, currentTS) + if err != nil { + log.Fatalf("Code: %v", err) + } + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", name, prev, curr, next) } tw.Flush() fmt.Printf("[%-29s]\n", strings.Repeat("=", progress)) } - -// normalizeSecret cleans up whitespace and adds any missing padding to sec to -// use it as an OTP seed. -func normalizeSecret(sec string) string { - noPadding := strings.ToUpper(strings.Replace(sec, " ", "", -1)) - padLength := 8 - (len(noPadding) % 8) - if padLength < 8 { - return noPadding + strings.Repeat("=", padLength) - } - return noPadding -} - -// authCodeOrDie returns a code for the specified parameters, or aborts if an -// error occurred while generating the code. -func authCodeOrDie(sec string, ts int64) string { - str, e := gauth.Code(sec, ts) - if e != nil { - log.Fatal(e) - } - return str -} diff --git a/gauth/gauth.go b/gauth/gauth.go index 5976ec8..34df302 100644 --- a/gauth/gauth.go +++ b/gauth/gauth.go @@ -3,12 +3,9 @@ package gauth import ( - "crypto/hmac" - "crypto/sha1" - "encoding/base32" - "fmt" - "math/big" "time" + + "bitbucket.org/creachadair/otp" ) // IndexNow returns the current 30-second time slice index, and the number of @@ -18,31 +15,16 @@ func IndexNow() (int64, int) { return time / 30, int(time % 30) } -// Code returns the OTP code for the given secret at the specified time slice -// index. It will report an error if the secret is not valid Base32 or if HMAC -// generation fails. -func Code(sec string, ts int64) (string, error) { - key, err := base32.StdEncoding.DecodeString(sec) - if err != nil { - return "", err +// Codes returns the OTP codes for the given secret at the specified time slice +// and one slice on either side of it. It will report an error if the secret is +// not valid Base32. +func Codes(sec string, ts int64) (prev, curr, next string, _ error) { + var cfg otp.Config + if err := cfg.ParseKey(sec); err != nil { + return "", "", "", err } - enc := hmac.New(sha1.New, key) - msg := make([]byte, 8) - msg[0] = (byte)(ts >> (7 * 8) & 0xff) - msg[1] = (byte)(ts >> (6 * 8) & 0xff) - msg[2] = (byte)(ts >> (5 * 8) & 0xff) - msg[3] = (byte)(ts >> (4 * 8) & 0xff) - msg[4] = (byte)(ts >> (3 * 8) & 0xff) - msg[5] = (byte)(ts >> (2 * 8) & 0xff) - msg[6] = (byte)(ts >> (1 * 8) & 0xff) - msg[7] = (byte)(ts >> (0 * 8) & 0xff) - if _, err := enc.Write(msg); err != nil { - return "", err - } - hash := enc.Sum(nil) - offset := hash[19] & 0x0f - trunc := hash[offset : offset+4] - trunc[0] &= 0x7F - res := new(big.Int).Mod(new(big.Int).SetBytes(trunc), big.NewInt(1000000)) - return fmt.Sprintf("%06d", res), nil + prev = cfg.HOTP(uint64(ts - 1)) + curr = cfg.HOTP(uint64(ts)) + next = cfg.HOTP(uint64(ts + 1)) + return } diff --git a/gauth/gauth_test.go b/gauth/gauth_test.go index 4bed176..ffe343e 100644 --- a/gauth/gauth_test.go +++ b/gauth/gauth_test.go @@ -6,7 +6,7 @@ import ( "github.com/pcarrier/gauth/gauth" ) -func TestCode(t *testing.T) { +func TestCodes(t *testing.T) { tests := []struct { secret string index int64 @@ -20,7 +20,7 @@ func TestCode(t *testing.T) { {"blargh!", 123, "", true}, } for _, test := range tests { - got, err := gauth.Code(test.secret, test.index) + _, got, _, err := gauth.Codes(test.secret, test.index) if err != nil && !test.fail { t.Errorf("Code(%q, %d): unexpected error: %v", test.secret, test.index, err) } else if got != test.want { diff --git a/go.mod b/go.mod index 35caacb..b0eaf0c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/pcarrier/gauth go 1.12 require ( + bitbucket.org/creachadair/otp v0.0.2 golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 // indirect ) diff --git a/go.sum b/go.sum index 808e7ec..ecd4f93 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +bitbucket.org/creachadair/otp v0.0.2 h1:0IAI6yUiapmEHt/x6sdmv8BnPRVOyWT/mPxqYkcYbF8= +bitbucket.org/creachadair/otp v0.0.2/go.mod h1:1RXxCIhHl/bxTZlSWONJzpCOi5uHZuwrDAkomlYrUGg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=