Merge pull request from creachadair/indirect

Consolidate and simplify the generation of codes.
This commit is contained in:
Pierre Carrier
2019-06-04 18:12:47 +02:00
committed by GitHub
5 changed files with 24 additions and 62 deletions

@ -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
}

@ -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
}

@ -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 {

1
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
)

2
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=