-
Notifications
You must be signed in to change notification settings - Fork 22
/
totp.go
89 lines (72 loc) · 2.3 KB
/
totp.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
84
85
86
87
88
89
package steam
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/binary"
"encoding/json"
"net/http"
)
const (
chars = "23456789BCDFGHJKMNPQRTVWXY"
charsLen = uint32(len(chars))
)
type ServerTimeTip struct {
Time int64 `json:"server_time,string"`
SkewToleranceSeconds uint32 `json:"skew_tolerance_seconds,string"`
LargeTimeJink uint32 `json:"large_time_jink,string"`
ProbeFrequencySeconds uint32 `json:"probe_frequency_seconds"`
AdjustedTimeProbeFrequencySeconds uint32 `json:"adjusted_time_probe_frequency_seconds"`
HintProbeFrequencySeconds uint32 `json:"hint_probe_frequency_seconds"`
SyncTimeout uint32 `json:"sync_timeout"`
TryAgainSeconds uint32 `json:"try_again_seconds"`
MaxAttempts uint32 `json:"max_attempts"`
}
func GenerateTwoFactorCode(sharedSecret string, current int64) (string, error) {
data, err := base64.StdEncoding.DecodeString(sharedSecret)
if err != nil {
return "", err
}
ful := make([]byte, 8)
binary.BigEndian.PutUint32(ful[4:], uint32(current/30))
hmac := hmac.New(sha1.New, data)
hmac.Write(ful)
sum := hmac.Sum(nil)
start := sum[19] & 0x0F
slice := binary.BigEndian.Uint32(sum[start:start+4]) & 0x7FFFFFFF
buf := make([]byte, 5)
for i := 0; i < 5; i++ {
buf[i] = chars[slice%charsLen]
slice /= charsLen
}
return string(buf), nil
}
func GenerateConfirmationCode(identitySecret, tag string, current int64) (string, error) {
data, err := base64.StdEncoding.DecodeString(identitySecret)
if err != nil {
return "", err
}
ful := make([]byte, 8+len(tag))
binary.BigEndian.PutUint32(ful[4:], uint32(current))
copy(ful[8:], tag)
hmac := hmac.New(sha1.New, data)
hmac.Write(ful)
return base64.StdEncoding.EncodeToString(hmac.Sum(nil)), nil
}
func GetTimeTip() (*ServerTimeTip, error) {
resp, err := http.Post("https://api.steampowered.com/ITwoFactorService/QueryTime/v1/", "application/x-www-form-urlencoded", nil)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
type Response struct {
Inner *ServerTimeTip `json:"response"`
}
var response Response
if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, err
}
return response.Inner, nil
}