-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhotp.go
53 lines (46 loc) · 999 Bytes
/
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
package hotp
import (
"crypto/hmac"
"crypto/sha1"
"encoding/binary"
"math"
)
const (
fourBitMask = 0xf
eightBitMask = 0xff
thirtyOneBitMask = 0x7fffffff
)
// Generator generates HMAC-based One-Time Password
type Generator struct {
Counter uint64 // C in RFC4226
Secret string // K in RFC4226
Digit int
}
// Generate HOTP
func (g *Generator) Generate() int64 {
hs := hmacSHA1([]byte(g.Secret), counterToBytes(g.Counter))
snum := truncate(hs)
d := int64(snum) % int64(math.Pow10(g.Digit))
g.Counter++
return d
}
func counterToBytes(c uint64) []byte {
t := make([]byte, 8)
for i := 7; i >= 0; i-- {
t[i] = byte(c & eightBitMask)
c = c >> 8
}
return t
}
func hmacSHA1(k, c []byte) (hs []byte) {
mac := hmac.New(sha1.New, k)
mac.Write(c)
hs = mac.Sum(nil)
return hs
}
func truncate(hs []byte) int {
offsetBits := hs[len(hs)-1] & fourBitMask
offset := int(offsetBits)
p := hs[offset : offset+4]
return int(binary.BigEndian.Uint32(p)) & thirtyOneBitMask
}