-
Notifications
You must be signed in to change notification settings - Fork 10
/
otp.go
67 lines (52 loc) · 1.78 KB
/
otp.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
package otpgo
import (
"crypto/hmac"
"crypto/rand"
"encoding/base32"
"encoding/binary"
"strings"
"github.com/jltorresm/otpgo/config"
)
// RandomKeyLength is the recommended length for the key used to generate OTPs.
// This length will be used to generate default keys (in HOTP.ensureKey and
// TOTP.ensureKey), when the caller does not provide one explicitly.
const RandomKeyLength = 64
var otpBase32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
// Generates a new OTP using the specified parameters based on the rfc4226.
func generateOTP(key string, counter uint64, length config.Length, algorithm config.HmacAlgorithm) (string, error) {
// Ensure key is uppercase
key = strings.ToUpper(key)
// Trim unnecessary paddings in case the key was generated externally.
key = strings.TrimRight(key, string(base32.StdPadding))
// Decode secret key to bytes
k, err := otpBase32Encoding.DecodeString(key)
if err != nil {
return "", ErrorInvalidKey{msg: err.Error()}
}
// Convert the counter to bytes
msg := make([]byte, 8)
binary.BigEndian.PutUint64(msg, counter)
// Start the hmac algorithm
hm := hmac.New(algorithm.Hash, k)
if _, err := hm.Write(msg); err != nil {
return "", err
}
sum := hm.Sum([]byte{})
// Build the result integer
offset := sum[len(sum)-1] & 0xf
bin := ((int(sum[offset]) & 0x7f) << 24) |
((int(sum[offset+1]) & 0xff) << 16) |
((int(sum[offset+2]) & 0xff) << 8) |
(int(sum[offset+3]) & 0xff)
rawOtp := length.Truncate(bin)
otp := length.LeftPad(rawOtp)
return otp, nil
}
// Generates a random key of the specified length, usable for OTP generation.
func randomKey(length uint) (string, error) {
buff := make([]byte, length)
if _, err := rand.Read(buff); err != nil {
return "", err
}
return otpBase32Encoding.EncodeToString(buff), nil
}