1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package main
import ( "crypto/hmac" "crypto/sha1" "encoding/base32" "encoding/binary" "errors" "fmt" "net/url" "testing" "time" )
type GoogleAuthenticator2FaSha1 struct { Base32NoPaddingEncodedSecret string ExpireSecond uint64 Digits int }
const testSecret = "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"
func (m *GoogleAuthenticator2FaSha1) Totp() (code string, err error) { count := uint64(time.Now().Unix()) / m.ExpireSecond key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(m.Base32NoPaddingEncodedSecret) if err != nil { return "", errors.New("https://github.com/google/google-authenticator/wiki/Key-Uri-Format,REQUIRED: The base32NoPaddingEncodedSecret parameter is an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.") } codeInt := hotp(key, count, m.Digits) intFormat := fmt.Sprintf("%%0%dd", m.Digits) return fmt.Sprintf(intFormat, codeInt), nil }
func (m *GoogleAuthenticator2FaSha1) QrString(label, issuer string) (qr string) { issuer = url.QueryEscape(label) return fmt.Sprintf(`otpauth://totp/%s?secret=%s&issuer=%s&algorithm=SHA1&digits=%d&period=%d`, label, m.Base32NoPaddingEncodedSecret, issuer, m.Digits, m.ExpireSecond) }
func hotp(key []byte, counter uint64, digits int) int { h := hmac.New(sha1.New, key) binary.Write(h, binary.BigEndian, counter) sum := h.Sum(nil) v := binary.BigEndian.Uint32(sum[sum[len(sum)-1]&0x0F:]) & 0x7FFFFFFF d := uint32(1) for i := 0; i < digits && i < 8; i++ { d *= 10 } return int(v % d) } func TestTotp(t *testing.T) { g := GoogleAuthenticator2FaSha1{ Base32NoPaddingEncodedSecret: testSecret, ExpireSecond: 30, Digits: 6, } totp, err := g.Totp() if err != nil { t.Error(err) return } t.Log(totp) }
func TestQr(t *testing.T) { g := GoogleAuthenticator2FaSha1{ Base32NoPaddingEncodedSecret: testSecret, ExpireSecond: 30, Digits: 6, } qrString := g.QrString("TechBlog:mojotv.cn", "Eric Zhou") t.Log(qrString) } func main() { a := GoogleAuthenticator2FaSha1{"HXDMVCCCJJWSRB3HWIZR4IFUGFTMXBOZ", 30, 6} b := a.QrString("fushisanlang", "dao.server") fmt.Println(b) escapeUrl := url.QueryEscape(b) fmt.Println("https://api.pwmqr.com/qrcode/create/?url=" + escapeUrl) }
|