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
|
package forge
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"encoding/base32"
"encoding/binary"
"encoding/hex"
"fmt"
"strings"
"time"
)
func newSecret() string {
b := make([]byte, 20)
rand.Read(b)
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
}
func totpCode(secret string, t time.Time) string {
key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(strings.ToUpper(secret))
if err != nil {
return ""
}
counter := make([]byte, 8)
binary.BigEndian.PutUint64(counter, uint64(t.Unix()/30))
mac := hmac.New(sha1.New, key)
mac.Write(counter)
sum := mac.Sum(nil)
offset := sum[len(sum)-1] & 0x0f
code := binary.BigEndian.Uint32(sum[offset:offset+4]) & 0x7fffffff
return fmt.Sprintf("%06d", code%1000000)
}
func totpValid(secret, code string) bool {
code = strings.ReplaceAll(strings.TrimSpace(code), " ", "")
now := time.Now()
for _, d := range []time.Duration{0, -30 * time.Second, 30 * time.Second} {
if hmac.Equal([]byte(totpCode(secret, now.Add(d))), []byte(code)) {
return true
}
}
return false
}
func newRecoveryCodes() ([]string, string) {
var codes, hashes []string
for i := 0; i < 8; i++ {
b := make([]byte, 5)
rand.Read(b)
code := hex.EncodeToString(b)
codes = append(codes, code[:5]+"-"+code[5:])
sum := sha256.Sum256([]byte(code))
hashes = append(hashes, hex.EncodeToString(sum[:]))
}
return codes, strings.Join(hashes, ",")
}
func useRecovery(stored, code string) (string, bool) {
code = strings.ToLower(strings.ReplaceAll(strings.TrimSpace(code), "-", ""))
sum := sha256.Sum256([]byte(code))
h := hex.EncodeToString(sum[:])
var kept []string
found := false
for _, x := range strings.Split(stored, ",") {
if x == h && !found {
found = true
continue
}
if x != "" {
kept = append(kept, x)
}
}
return strings.Join(kept, ","), found
}
|