-
Notifications
You must be signed in to change notification settings - Fork 0
/
hotp.go
76 lines (65 loc) · 1.41 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
package hotp
import (
"crypto/hmac"
"encoding/binary"
"errors"
"hash"
"math"
)
const (
counterSize = 8 // uint64
DefaultDigit = 6 // the number of HOTP digit
Version = "0.1.0" // out first version
)
var (
ErrHMAC = errors.New("HMAC error")
)
type Hotp struct {
secret []byte
digit uint32
hm hash.Hash
}
func New(f func() hash.Hash, s []byte, d int) *Hotp {
digit := DefaultDigit
// 6, 7 or 8 digits!!
switch d {
case 7, 8:
digit = d
}
h := &Hotp{
secret: s, // the secret
digit: uint32(math.Pow(float64(10), float64(digit))), // the modulo
hm: hmac.New(f, s), // the hmac function setup
}
return h
}
// internal it returns the hash
func (h *Hotp) hmacCounter(c uint64) (out []byte, err error) {
buf := make([]byte, counterSize)
binary.BigEndian.PutUint64(buf, c)
n, err := h.hm.Write(buf)
if err != nil || n != counterSize {
return
}
out = h.hm.Sum(nil)
return
}
// should work if SHA256 or SHA512
func (h *Hotp) dt(hash []byte) uint32 {
/* TODO handle error
if len(hash) < 20 {
}
*/
offset := hash[len(hash)-1] & 0x0f
dbc1 := hash[offset : offset+4]
dbc1[0] = dbc1[0] & 0x7f
dbc2 := binary.BigEndian.Uint32(dbc1)
return uint32(dbc2 % h.digit)
}
func (h *Hotp) Get(c uint64) (uint32, error) {
out, err := h.hmacCounter(c)
if err != nil {
return 0, ErrHMAC
}
return h.dt(out), nil
}