github.com/godaddy-x/freego@v1.0.156/utils/gauth/google_auth.go (about) 1 package gauth 2 3 import ( 4 "crypto/hmac" 5 "crypto/sha1" 6 "crypto/sha512" 7 "encoding/base32" 8 "encoding/base64" 9 "fmt" 10 "github.com/godaddy-x/freego/utils" 11 "time" 12 ) 13 14 const ( 15 windowSize = 3 16 secretSize = 10 17 base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 18 // seed = "g8GjEvTbW5oVSV7avL47357438reyhreyuryetredLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx" 19 ) 20 21 //transform int64 to []byte 22 func toBytes(value int64) []byte { 23 var result []byte 24 mask := int64(0xFF) 25 shifts := [8]uint16{56, 48, 40, 32, 24, 16, 8, 0} 26 for _, shift := range shifts { 27 result = append(result, byte((value>>shift)&mask)) 28 } 29 return result 30 } 31 32 //transform []byte to uint32 33 func toUint32(bytes []byte) uint32 { 34 return (uint32(bytes[0]) << 24) + (uint32(bytes[1]) << 16) + 35 (uint32(bytes[2]) << 8) + uint32(bytes[3]) 36 } 37 38 func SHA1PRNG(seed []byte, size int) ([]byte, error) { 39 hashs := SHA1(SHA1(seed)) 40 maxLen := len(hashs) 41 realLen := size 42 if realLen > maxLen { 43 return nil, fmt.Errorf("Not Support %d, Only Support Lower then %d [% x]", realLen, maxLen, hashs) 44 } 45 return hashs[0:realLen], nil 46 } 47 48 func SHA1(data []byte) []byte { 49 h := sha1.New() 50 h.Write(data) 51 return h.Sum(nil) 52 } 53 54 func SHA512(data []byte) []byte { 55 h := sha512.New() 56 h.Write(data) 57 return h.Sum(nil) 58 } 59 60 //生成随机密钥,输出结果是经过base32编码 61 func GenerateSecretKey(seed string) (string, error) { 62 coder := base64.NewEncoding(base64Table) 63 decodeSeed, err := coder.DecodeString(seed) 64 if err != nil { 65 return "", err 66 } 67 // fmt.Println("decodeSeed:", hex.EncodeToString(decodeSeed)) 68 key, err := SHA1PRNG(decodeSeed, secretSize) 69 if err != nil { 70 return "", err 71 } 72 // fmt.Println("key:", hex.EncodeToString(key)) 73 maxLen := base32.StdEncoding.EncodedLen(len(key)) 74 ret := make([]byte, maxLen) 75 base32.StdEncoding.Encode(ret, key) 76 return utils.Bytes2Str(ret), nil 77 } 78 79 /* 80 * @生成code验证码 81 * @secretKey 密钥 82 * @curTimeSlice 当前时间戳 - time.Now().Unix() 83 * @valid 是否校验操作 true.是 false.否 84 */ 85 func generateCode(secretKey string, curTimeSlice int64, valid bool) uint32 { 86 //base32 解码 87 secretKeyBytes, _ := base32.StdEncoding.DecodeString(secretKey) 88 //转换curTimeSlice为byte数组 89 counterByBytes := toBytes(curTimeSlice) 90 if !valid { 91 counterByBytes = toBytes(curTimeSlice / 30) 92 } 93 //sign the value using hmac-sha1 94 hmacSha1 := hmac.New(sha1.New, secretKeyBytes) 95 hmacSha1.Write(counterByBytes) 96 hash := hmacSha1.Sum(nil) 97 //the next to get a subset of the generated hash as the password 98 //choose the index:using the last nibble(half byte)to choose the index to start from 99 offset := hash[len(hash)-1] & 0x0F //Due to hmac-sha1,len(hash)-1 = 19 100 //get a 32-bit chunk from the hash starting at offset 101 hashParts := hash[offset : offset+4] 102 //ignore the most significant bit as per RFC 4226 103 hashParts[0] = hashParts[0] & 0x7F 104 //transform hashParts to uint32 105 number := toUint32(hashParts) 106 // size to 6 digits, one million is the first number with 7 digits so the remainder of the division will always return < 7 digits 107 pwd := number % 1000000 108 return pwd 109 } 110 111 /* 112 * @生成code验证码 113 * @secretKey 密钥 114 * @curTimeSlice 当前时间戳 - time.Now().Unix() 115 */ 116 func GetNewCode(secretKey string, curTimeSlice int64) uint32 { 117 return generateCode(secretKey, curTimeSlice, false) 118 } 119 120 /* 121 * @校验code有效性 122 * @secretKey 密钥 123 * @code 验证码 124 * @size 偏移时间 s >= 1 && s <= 17 125 */ 126 func ValidCode(secretKey string, code uint32, size ...int) bool { 127 //conver uinx msec time into a 30 second "window" 128 curTimeSlice := time.Now().Unix() / 30 129 //window is used to check codes generated in the near past 130 wsize := windowSize //可偏移时间,default=3, max=17 131 if size != nil && len(size) == 1 { 132 wsize = size[0] 133 } 134 if wsize < 1 || wsize > 17 { 135 return false 136 } 137 for i := -wsize; i <= wsize; i++ { 138 hash := generateCode(secretKey, (curTimeSlice + int64(i)), true) 139 if hash == code { 140 return true 141 } 142 } 143 return false 144 } 145 146 /* 147 * @生成密钥种子 148 */ 149 func GenerateSeed() string { 150 hash := SHA512(toBytes(utils.NextIID())) 151 coder := base64.NewEncoding(base64Table) 152 ret := coder.EncodeToString(hash) 153 return ret 154 }