/
hotp.go
83 lines (71 loc) · 1.33 KB
/
hotp.go
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
package entica
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base32"
"encoding/binary"
"hash"
"strconv"
"strings"
)
type HOTP struct {
OTP
Counter int
byteSecret []byte
}
func NewHOTP(s string) *HOTP {
return NewHOTPSHA(defaultDigit, sha1.New, s)
}
func NewHOTP256(s string) *HOTP {
return NewHOTPSHA(defaultDigit, sha256.New, s)
}
func NewHOTP512(s string) *HOTP {
return NewHOTPSHA(defaultDigit, sha512.New, s)
}
func NewHOTPSHA(digits int, hash func() hash.Hash, secret string) *HOTP {
s := strings.ToUpper(secret)
byteSecret, _ := base32.StdEncoding.DecodeString(s)
return &HOTP{
OTP: OTP{
Hash: hash,
Digits: digits,
Secret: s,
},
Counter: 1,
byteSecret: byteSecret,
}
}
func (h *HOTP) SetCounter(c int) {
h.Counter = c
}
func (h *HOTP) Get() string {
code := h.Current()
h.Counter++
return code
}
func (h *HOTP) toBytes(c int) []byte {
bytes := make([]byte, 8)
num, _ := strconv.ParseUint(strconv.Itoa(c), 0, 64)
binary.BigEndian.PutUint64(bytes, num)
return bytes
}
func (h *HOTP) AtCounter(c int) string {
return h.genOTP(
h.byteSecret,
h.toBytes(c),
h.Digits,
h.Hash,
)
}
func (h *HOTP) Current() string {
return h.genOTP(
h.byteSecret,
h.toBytes(h.Counter),
h.Digits,
h.Hash,
)
}
func (h *HOTP) Compare(code string) bool {
return h.Get() == code
}