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  }