remove ginext/mongoext (no-dep lib)
This commit is contained in:
89
totpext/totp.go
Normal file
89
totpext/totp.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package totpext
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc6238
|
||||
// https://datatracker.ietf.org/doc/html/rfc4226
|
||||
// https://datatracker.ietf.org/doc/html/rfc2104
|
||||
|
||||
// https://en.wikipedia.org/wiki/Universal_2nd_Factor
|
||||
// https://en.wikipedia.org/wiki/HMAC-based_one-time_password
|
||||
// https://en.wikipedia.org/wiki/HMAC
|
||||
|
||||
func TOTP(key []byte) string {
|
||||
t := time.Now().Unix() / 30
|
||||
return generateTOTP(sha1.New, key, t, 6)
|
||||
}
|
||||
|
||||
func Validate(key []byte, totp string) bool {
|
||||
t := time.Now().Unix() / 30
|
||||
|
||||
if generateTOTP(sha1.New, key, t, 6) == totp {
|
||||
return true
|
||||
}
|
||||
|
||||
if generateTOTP(sha1.New, key, t-1, 6) == totp {
|
||||
return true
|
||||
}
|
||||
|
||||
if generateTOTP(sha1.New, key, t+1, 6) == totp {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func GenerateSecret() ([]byte, error) {
|
||||
secret := make([]byte, 20)
|
||||
_, err := rand.Read(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func generateTOTP(algo func() hash.Hash, secret []byte, time int64, returnDigits int) string {
|
||||
msg := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(msg, uint64(time))
|
||||
|
||||
mac := hmac.New(algo, secret)
|
||||
mac.Write(msg)
|
||||
hmacResult := mac.Sum(nil)
|
||||
|
||||
offsetBits := hmacResult[len(hmacResult)-1] & 0x0F
|
||||
|
||||
p := hmacResult[offsetBits : offsetBits+4]
|
||||
|
||||
truncated := binary.BigEndian.Uint32(p) & 0x7FFFFFFF // Last 31 bits
|
||||
|
||||
val := strconv.Itoa(int(truncated))
|
||||
for len(val) < returnDigits {
|
||||
val = "0" + val
|
||||
}
|
||||
val = val[len(val)-returnDigits:]
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func GenerateOTPAuth(ccn string, key []byte, accountmail string, issuer string) string {
|
||||
|
||||
return fmt.Sprintf("otpauth://totp/%v:%v?secret=%v&issuer=%v&algorithm=%v&period=%v&digits=%v",
|
||||
ccn,
|
||||
url.QueryEscape(accountmail),
|
||||
base32.StdEncoding.EncodeToString(key),
|
||||
issuer,
|
||||
"SHA1",
|
||||
"30",
|
||||
"6")
|
||||
}
|
Reference in New Issue
Block a user