/
tokens.go
115 lines (91 loc) · 2.56 KB
/
tokens.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package tokens
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
prng "github.com/EricLagerg/go-prng/xorshift"
"github.com/golang/glog"
)
const (
HashSize = 32
// We exclude '0' here because casting a byte slice/array that contains
// a '0' to a string will cause it to have a null byte. On Unix --
// and I'm assuming Windows -- platforms a null byte denotes the end
// of the string. This causes syscalls like Create or Open to break
// because the string is interpreted incorrectly.
charTable = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
tableLen = uint64(len(charTable) - 1)
)
var (
CSRFKey = []byte("0655A28CAAEB0448132026D863771C5D")
ErrGeneratedBadToken = errors.New("Generated a token with an invalid length.")
)
// NewAuthToken returns a hex-encoded byte slice with a length of 64.
// The token is generated by reading from the OS' PRNG source, usually
// /dev/urandom on Unix-like systems and the CryptGenRandom API on
// Windows.
func NewAuthToken() []byte {
return newSecureToken()
}
func NewSessionID() string {
return string(newSecureToken())
}
// NewCSRFToken returns a byte slice with a new base-64 encoded CSRF token,
// only valid for a user's session.
func NewCSRFToken(id string) []byte {
mac := hmac.New(sha256.New, CSRFKey)
_, err := mac.Write([]byte(id))
if err != nil {
glog.Fatalln(err)
}
src := mac.Sum(nil)
buf := make([]byte, base64.StdEncoding.EncodedLen(len(src)))
base64.URLEncoding.Encode(buf, src)
return buf
}
func newSecureToken() []byte {
buf := make([]byte, HashSize)
n, err := io.ReadFull(rand.Reader, buf)
if err != nil {
glog.Fatalln(err)
}
if n != HashSize {
glog.Fatalln(ErrGeneratedBadToken)
}
tok := make([]byte, hex.EncodedLen(len(buf)))
if hex.Encode(tok, buf) != 64 {
glog.Fatalln(ErrGeneratedBadToken)
}
return tok
}
var (
r = new(prng.Shift128Plus) // Our xorshift PRNG
_u uint64 // Prevent Go from optimizing out the warmup.
)
func init() {
r.Seed()
// Warm up the PRNG. It doesn't necessarily need it, but it won't
// hurt.
var n uint64
fmt.Println("Starting PRNG warmup. MAKE SURE TO CHANGE THIS.")
for i := 0; i < 1; i++ { //e9; i++ {
n = r.Next()
}
_u = n
fmt.Printf("Ended PRNG warmup with value of %d.\n", n)
}
// Create fast random strings for job IDs.
// Made up to 29 million unique strings before I quit testing.
func NewJobID() string {
var buf [32]byte
for i := HashSize - 1; i >= 0; i-- {
key := r.Next() % tableLen
buf[i] = charTable[key]
}
return string(buf[:])
}