File view with blame information shown in the left gutter beside each line.

barerepo / server / internal/token/token.go
56 lines · 1.6kb · master
log files threads runs releases config jump to file t
133728e barerepo 1mo
1
// Package token makes item 3's credentials, stores only a hash, and shows each once.
133728e barerepo 1mo
2
package token
133728e barerepo 1mo
3
133728e barerepo 1mo
4
import (
133728e barerepo 1mo
5
"crypto/rand"
133728e barerepo 1mo
6
"crypto/sha256"
133728e barerepo 1mo
7
"crypto/subtle"
133728e barerepo 1mo
8
"encoding/base64"
133728e barerepo 1mo
9
"encoding/hex"
133728e barerepo 1mo
10
"fmt"
133728e barerepo 1mo
11
"strings"
133728e barerepo 1mo
12
)
133728e barerepo 1mo
13
133728e barerepo 1mo
14
// Kind is a token's scope and its visible prefix, so a leaked one can be found by grep.
133728e barerepo 1mo
15
type Kind string
133728e barerepo 1mo
16
133728e barerepo 1mo
17
const (
133728e barerepo 1mo
18
Git Kind = "gt" // push and clone over https
133728e barerepo 1mo
19
Runner Kind = "rt" // one machine, one repository
133728e barerepo 1mo
20
Feed Kind = "ft" // read one atom feed, and nothing else
133728e barerepo 1mo
21
Session Kind = "se" // a browser session cookie
133728e barerepo 1mo
22
Claim Kind = "cl" // one use, trades a signed nonce for a browser session
133728e barerepo 1mo
23
)
133728e barerepo 1mo
24
133728e barerepo 1mo
25
func (k Kind) prefix() string { return string(k) + "_live_" }
133728e barerepo 1mo
26
133728e barerepo 1mo
27
// New returns the only copy of a token, plus the hash to store, and nothing recovers it later.
133728e barerepo 1mo
28
func New(k Kind) (tok, hash string, err error) {
133728e barerepo 1mo
29
buf := make([]byte, 16)
133728e barerepo 1mo
30
if _, err := rand.Read(buf); err != nil {
133728e barerepo 1mo
31
return "", "", err
133728e barerepo 1mo
32
}
133728e barerepo 1mo
33
tok = k.prefix() + base64.RawURLEncoding.EncodeToString(buf)
133728e barerepo 1mo
34
return tok, Hash(tok), nil
133728e barerepo 1mo
35
}
133728e barerepo 1mo
36
133728e barerepo 1mo
37
// Hash is unsalted SHA-256, right for 128 machine-generated bits with no dictionary to fear.
133728e barerepo 1mo
38
func Hash(tok string) string {
133728e barerepo 1mo
39
sum := sha256.Sum256([]byte(tok))
133728e barerepo 1mo
40
return hex.EncodeToString(sum[:])
133728e barerepo 1mo
41
}
133728e barerepo 1mo
42
133728e barerepo 1mo
43
// KindOf reads the claimed kind, so a lookup can be scoped before it happens.
133728e barerepo 1mo
44
func KindOf(tok string) (Kind, error) {
133728e barerepo 1mo
45
for _, k := range []Kind{Git, Runner, Feed, Session, Claim} {
133728e barerepo 1mo
46
if strings.HasPrefix(tok, k.prefix()) {
133728e barerepo 1mo
47
return k, nil
133728e barerepo 1mo
48
}
133728e barerepo 1mo
49
}
133728e barerepo 1mo
50
return "", fmt.Errorf("that does not look like a barerepo token")
133728e barerepo 1mo
51
}
133728e barerepo 1mo
52
133728e barerepo 1mo
53
// Equal compares two hashes in constant time.
133728e barerepo 1mo
54
func Equal(a, b string) bool {
133728e barerepo 1mo
55
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
133728e barerepo 1mo
56
}
history · rawbarerepo 0.1.0