simpleauth/pkg/token/token.go

84 lines
1.6 KiB
Go

package token
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"log"
"time"
)
type T struct {
expiration time.Time
mac []byte
}
func (t T) computeMac(secret []byte) []byte {
mac := hmac.New(sha256.New, secret)
binary.Write(mac, binary.BigEndian, t.expiration)
return mac.Sum([]byte{})
}
// String returns the string encoding of the token
func (t T) String() string {
f := new(bytes.Buffer)
if err := binary.Write(f, binary.BigEndian, t.expiration.Unix()); err != nil {
log.Fatal(err)
}
f.Write(t.mac)
return base64.StdEncoding.EncodeToString(f.Bytes())
}
// Valid returns true iff the token is valid for the given secret and current time
func (t T) Valid(secret []byte) bool {
if time.Now().After(t.expiration) {
return false
}
if !hmac.Equal(t.mac, t.computeMac(secret)) {
return false
}
return true
}
// New returns a new token
func New(secret []byte, expiration time.Time) T {
t := T{
expiration: expiration,
}
t.mac = t.computeMac(secret)
return t
}
// Parse returns a new token from the given bytes
func Parse(b []byte) (T, error) {
t := T{
mac: make([]byte, sha256.Size),
}
f := bytes.NewReader(b)
{
var sec int64
if err := binary.Read(f, binary.BigEndian, &sec); err != nil {
return t, err
}
t.expiration = time.Unix(sec, 0)
}
if n, err := f.Read(t.mac); err != nil {
return t, err
} else {
t.mac = t.mac[:n]
}
return t, nil
}
// ParseString parses a base64-encoded string, as created by T.String()
func ParseString(s string) (T, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return T{}, nil
}
return Parse(b)
}