Step 3: Compute an HOTP value
Let Snum = StToNum(Sbits) // Convert S to a number in
0...2^{31}-1
Return D = Snum mod 10^Digit // D is a number in the range
0...10^{Digit}-1
// Generate creates a new HOTP Key.
funcGenerate(optsGenerateOpts)(*otp.Key,error){// url encode the Issuer/AccountName
ifopts.Issuer==""{returnnil,otp.ErrGenerateMissingIssuer}ifopts.AccountName==""{returnnil,otp.ErrGenerateMissingAccountName}ifopts.SecretSize==0{opts.SecretSize=10}ifopts.Digits==0{opts.Digits=otp.DigitsSix}ifopts.Rand==nil{opts.Rand=rand.Reader}// otpauth://hotp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
v:=url.Values{}iflen(opts.Secret)!=0{v.Set("secret",b32NoPadding.EncodeToString(opts.Secret))}else{secret:=make([]byte,opts.SecretSize)_,err:=opts.Rand.Read(secret)iferr!=nil{returnnil,err}v.Set("secret",b32NoPadding.EncodeToString(secret))}v.Set("issuer",opts.Issuer)v.Set("algorithm",opts.Algorithm.String())v.Set("digits",opts.Digits.String())u:=url.URL{Scheme:"otpauth",Host:"hotp",Path:"/"+opts.Issuer+":"+opts.AccountName,RawQuery:internal.EncodeQuery(v),}returnotp.NewKeyFromURL(u.String())}
funcGenerateCodeCustom(secretstring,ttime.Time,optsValidateOpts)(passcodestring,errerror){ifopts.Period==0{opts.Period=30}// 生成计数器counter
counter:=uint64(math.Floor(float64(t.Unix())/float64(opts.Period)))// 调用hotp.GenerateCodeCustom()函数传入secret,计数器
passcode,err=hotp.GenerateCodeCustom(secret,counter,hotp.ValidateOpts{Digits:opts.Digits,Algorithm:opts.Algorithm,})iferr!=nil{return"",err}returnpasscode,nil}// 下面是htop的GenerateCodeCustom()函数
// 接受secret,counter和配置
funcGenerateCodeCustom(secretstring,counteruint64,optsValidateOpts)(passcodestring,errerror){//设置code的位数,默认是6位
ifopts.Digits==0{opts.Digits=otp.DigitsSix}// As noted in issue #10 and #17 this adds support for TOTP secrets that are
// missing their padding.
// 如果secret不是8的整数,用=补齐
secret=strings.TrimSpace(secret)ifn:=len(secret)%8;n!=0{secret=secret+strings.Repeat("=",8-n)}// As noted in issue #24 Google has started producing base32 in lower case,
// but the StdEncoding (and the RFC), expect a dictionary of only upper case letters.
secret=strings.ToUpper(secret)//将secret编码为base32的字节切片
secretBytes,err:=base32.StdEncoding.DecodeString(secret)iferr!=nil{return"",otp.ErrValidateSecretInvalidBase32}buf:=make([]byte,8)// 利用hamc函数生成sum
mac:=hmac.New(opts.Algorithm.Hash,secretBytes)binary.BigEndian.PutUint64(buf,counter)ifdebug{fmt.Printf("counter=%v\n",counter)fmt.Printf("buf=%v\n",buf)}mac.Write(buf)// 这里sum就是根据hmac-sha1生成的20字节数据切片,格式是
// [88 34 93 234 103 118 184 221 224 36 35 171 23 5 35 93 130 251 225 119]
// 用函数 fmt.Println(hex.EncodeToString(sum))转成16进制字符串,格式是
// 58225dea6776b8dde02423ab1705235d82fbe177
sum:=mac.Sum(nil)// "Dynamic truncation" in RFC 4226
// http://tools.ietf.org/html/rfc4226#section-5.4
//根据RFC4226规定得到偏移量为7,并截取4个字节数据 221 224 36 35进行如下算法运算
offset:=sum[len(sum)-1]&0xfvalue:=int64(((int(sum[offset])&0x7f)<<24)|((int(sum[offset+1]&0xff))<<16)|((int(sum[offset+2]&0xff))<<8)|(int(sum[offset+3])&0xff))l:=opts.Digits.Length()// 得到value 为 1574970403
mod:=int32(value%int64(math.Pow10(l)))// 将value进行取余得到mod为970403,即为生成的code
ifdebug{fmt.Printf("offset=%v\n",offset)fmt.Printf("value=%v\n",value)fmt.Printf("mod'ed=%v\n",mod)}returnopts.Digits.Format(mod),nil}