Files
web_huakui_server/pub/common/googauth/htop.go
T
rootandClaude Opus 4.7 85c0071fd8 feat: 从黄果 NET hgdjnet-server 9bfbafe 导入 HKDJ 官网后端
来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83
项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet
测试目录 /server/hkdj_net,App/Web API 30331/30332

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-16 11:20:04 +08:00

68 lines
1.5 KiB
Go

package googauth
import (
"crypto/hmac"
"crypto/sha1"
"encoding/binary"
"strconv"
)
// HOTP implementation
type HOTP struct {
secret []byte
digits int
}
// At generate code
func (h HOTP) At(counter uint64) string {
counterBytes := make([]byte, 8)
binary.BigEndian.PutUint64(counterBytes, counter)
hash := hmac.New(sha1.New, h.secret)
hash.Write(counterBytes)
hs := hash.Sum(nil)
offset := hs[19] & 0x0f
binCodeBytes := make([]byte, 4)
binCodeBytes[0] = hs[offset] & 0x7f
binCodeBytes[1] = hs[offset+1] & 0xff
binCodeBytes[2] = hs[offset+2] & 0xff
binCodeBytes[3] = hs[offset+3] & 0xff
binCode := binary.BigEndian.Uint32(binCodeBytes)
mod := uint32(1)
for i := 0; i < h.digits; i++ {
mod *= 10
}
code := binCode % mod
codeString := strconv.FormatUint(uint64(code), 10)
if len(codeString) < h.digits {
paddingByteLength := h.digits - len(codeString)
paddingBytes := make([]byte, paddingByteLength)
for i := 0; i < paddingByteLength; i++ {
paddingBytes[i] = '0'
}
codeString = string(paddingBytes) + codeString
}
return codeString
}
// Verify verify OTP code
func (h HOTP) VerifyCode(code string, counter uint64) bool {
realCode := h.At(counter)
if realCode == code {
return true
}
return false
}
// Verify verify OTP code
func (h HOTP) GenerateCode(counter uint64) string {
return h.At(counter)
}
// NewHOTP generate new HOTP instance
func NewHOTP(secret []byte, digits int) (h *HOTP) {
h = new(HOTP)
h.secret = secret
h.digits = digits
return
}