73 lines
1.5 KiB
Go
73 lines
1.5 KiB
Go
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
|
|
}
|