-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathsym_key_service.go
101 lines (79 loc) · 2.46 KB
/
sym_key_service.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
package isokey
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"time"
"github.com/jbenet/go-base58"
)
const symKeyDigestSize = 16 + 4 + 4 + 4 + 4 + 4
var _ = Signer(new(SymKeyService))
var _ = Verifier(new(SymKeyService))
//SymKeyService facilitates the creation and verification of symmetricly signed (HMAC) keys
type SymKeyService struct {
//GetSecret allows you to dynamically use secrets.
//Returning nil indicates that no secret was found for the key
GetSecret func(key *Key) (secret []byte)
//Invalidator allows you to dynamically invalidate a key.
//Invalidator is ran after the key's signature has been validated.
Invalidator func(*Key) bool
}
//NewSymKeyService returns a new sym key service using a single key
func NewSymKeyService(secret []byte) *SymKeyService {
return &SymKeyService{
GetSecret: func(key *Key) []byte {
return secret
},
}
}
//Invalid returns true if the key is invalid.
func (ks *SymKeyService) Invalid(key *Key) bool {
if ks.Invalidator == nil {
return key.ExpiresAt.Before(time.Now())
}
return key.ExpiresAt.Before(time.Now()) || ks.Invalidator(key)
}
//Verify securely validates a digest.
func (ks *SymKeyService) Verify(digest string) (*Key, error) {
key := &Key{}
rawDigest := base58.Decode(digest)
if len(rawDigest) != symKeyDigestSize {
return key, ErrSymKeySize
}
signature := rawDigest[:16]
key = unpack(rawDigest[16:])
secret := ks.GetSecret(key)
if secret == nil {
return nil, ErrNoSecret
}
if !checkMAC(rawDigest[16:], signature, secret) {
return key, ErrBadSignature
}
if ks.Invalid(key) {
return key, ErrInvalid
}
return key, nil
}
//Sign converts the key into it's base58 form.
//the only error that will be returned is ErrNoSecret.
//if key.MadeAt is zero it is set to the current time.
func (ks *SymKeyService) Sign(key *Key) (digest string, err error) {
message := &bytes.Buffer{}
if key.MadeAt.IsZero() {
key.MadeAt = time.Now()
}
binary.Write(message, binary.BigEndian, int32(key.MadeAt.Unix()))
binary.Write(message, binary.BigEndian, int32(key.ExpiresAt.Unix()))
binary.Write(message, binary.BigEndian, key.SecretVersion)
binary.Write(message, binary.BigEndian, key.UserID)
binary.Write(message, binary.BigEndian, key.Flags)
secret := ks.GetSecret(key)
if secret == nil {
return "", ErrNoSecret
}
mac := hmac.New(sha256.New, secret)
mac.Write(message.Bytes())
signature := mac.Sum(nil)[:16]
return base58.Encode(append(signature, message.Bytes()...)), nil
}