Created
December 18, 2018 11:52
-
-
Save SoulSu/8971e8b631a88da12fd8b58b3387a39c to your computer and use it in GitHub Desktop.
生成谷歌验证器
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tools | |
// 谷歌验证 | |
// clone from: https://github.com/928799934/googleAuthenticator | |
import ( | |
"crypto/hmac" | |
"crypto/sha1" | |
"encoding/base32" | |
"encoding/hex" | |
"errors" | |
"fmt" | |
"math" | |
"math/rand" | |
"strconv" | |
"strings" | |
"time" | |
) | |
var ( | |
ErrGaSecretLengthLss = errors.New("secret length lss 6 error") | |
ErrGaSecretLength = errors.New("secret length error") | |
ErrGaPaddingCharCount = errors.New("padding char count error") | |
ErrGaPaddingCharLocation = errors.New("padding char Location error") | |
ErrGaParam = errors.New("param error") | |
) | |
var ( | |
gaTable = []string{ | |
"A", "B", "C", "D", "E", "F", "G", "H", // 7 | |
"I", "J", "K", "L", "M", "N", "O", "P", // 15 | |
"Q", "R", "S", "T", "U", "V", "W", "X", // 23 | |
"Y", "Z", "2", "3", "4", "5", "6", "7", // 31 | |
"=", // padding char | |
} | |
gaallowedValues = map[int]string{ | |
6: "======", | |
4: "====", | |
3: "===", | |
1: "=", | |
0: "", | |
} | |
gaHmacSha1 = func(key, data []byte) []byte { | |
mac := hmac.New(sha1.New, key) | |
mac.Write(data) | |
return mac.Sum(nil) | |
} | |
) | |
func arrayFlip(oldArr []string) map[string]int { | |
newArr := make(map[string]int, len(oldArr)) | |
for key, value := range oldArr { | |
newArr[value] = key | |
} | |
return newArr | |
} | |
type GAuth struct { | |
codeLen float64 | |
table map[string]int | |
} | |
func NewGAuth() *GAuth { | |
return &GAuth{ | |
codeLen: 6, | |
table: arrayFlip(gaTable), | |
} | |
} | |
// SetCodeLength Set the code length, should be >=6 | |
func (this *GAuth) SetCodeLength(length float64) error { | |
if length < 6 { | |
return ErrGaSecretLengthLss | |
} | |
this.codeLen = length | |
return nil | |
} | |
// CreateSecret create new secret | |
// 16 characters, randomly chosen from the allowed base32 characters. | |
func (this *GAuth) CreateSecret(lens ...int) (string, error) { | |
var ( | |
length int | |
secret []string | |
) | |
// init length | |
switch len(lens) { | |
case 0: | |
length = 16 | |
case 1: | |
length = lens[0] | |
default: | |
return "", ErrGaParam | |
} | |
for i := 0; i < length; i++ { | |
secret = append(secret, gaTable[rand.Intn(len(gaTable))]) | |
} | |
return strings.Join(secret, ""), nil | |
} | |
// VerifyCode Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now | |
func (this *GAuth) VerifyCode(secret, code string, discrepancy int64) (bool, error) { | |
// now time | |
curTimeSlice := time.Now().Unix() / 30 | |
for i := -discrepancy; i <= discrepancy; i++ { | |
calculatedCode, err := this.GetCode(secret, curTimeSlice+i) | |
if err != nil { | |
return false, err | |
} | |
if calculatedCode == code { | |
return true, nil | |
} | |
} | |
return false, nil | |
} | |
// GetCode Calculate the code, with given secret and point in time | |
func (this *GAuth) GetCode(secret string, timeSlices ...int64) (string, error) { | |
var timeSlice int64 | |
switch len(timeSlices) { | |
case 0: | |
timeSlice = time.Now().Unix() / 30 | |
case 1: | |
timeSlice = timeSlices[0] | |
default: | |
return "", ErrGaParam | |
} | |
secret = strings.ToUpper(secret) | |
secretKey, err := base32.StdEncoding.DecodeString(secret) | |
if err != nil { | |
return "", err | |
} | |
tim, err := hex.DecodeString(fmt.Sprintf("%016x", timeSlice)) | |
if err != nil { | |
return "", err | |
} | |
hm := gaHmacSha1(secretKey, tim) | |
offset := hm[len(hm)-1] & 0x0F | |
hashpart := hm[offset : offset+4] | |
value, err := strconv.ParseInt(hex.EncodeToString(hashpart), 16, 0) | |
if err != nil { | |
return "", err | |
} | |
value = value & 0x7FFFFFFF | |
modulo := int64(math.Pow(10, this.codeLen)) | |
format := fmt.Sprintf("%%0%dd", int(this.codeLen)) | |
return fmt.Sprintf(format, value%modulo), nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment