package main import ( "crypto/hmac" "crypto/sha1" "crypto/sha256" "encoding/base32" "encoding/base64" "encoding/binary" "errors" "fmt" "hash" "math" "math/rand" "strings" "time" ) func NewSalt() string { var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, 4) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } func ComputeHmac256(message string, secret string) []byte { h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(message)) return h.Sum(nil) } func encode64(secret []byte) string { return strings.TrimRight(base64.StdEncoding.EncodeToString(secret), "=") } func encodeSecret(secret []byte) string { return strings.TrimRight(base32.StdEncoding.EncodeToString(secret), "=") } func GenericTotpCode(secret string, t time.Time, algo string, digits int, period int) (string, error) { var mac hash.Hash var format string now := [8]byte{} binary.BigEndian.PutUint64(now[:], uint64(math.Floor(float64(t.Unix())/float64(period)))) binsecret, _ := base32.StdEncoding.DecodeString(secret) switch algo { case "sha1": mac = hmac.New(sha1.New, binsecret) case "sha256": mac = hmac.New(sha256.New, binsecret) default: return "", errors.New("unsupported algorithm") } switch digits { case 6: format = "%06d" case 8: format = "%08d" default: return "", errors.New("unsupported code size") } mac.Write(now[:]) sum := mac.Sum(nil) return fmt.Sprintf(format, (binary.BigEndian.Uint32(sum[sum[len(sum)-1]&0xf:])&0x7fffffff)%1000000), nil }