Raw
 1package web
 2
 3import (
 4	"crypto/hkdf"
 5	"crypto/hmac"
 6	"crypto/sha256"
 7	"encoding/base64"
 8	"errors"
 9	"strings"
10)
11
12var errSeal = errors.New("sealed value rejected")
13
14func deriveKey(master []byte, info string) (*[]byte, error) {
15	k, err := hkdf.Key(sha256.New, master, nil, info, 32)
16	if err != nil {
17		return nil, err
18	}
19	return &k, nil
20}
21
22func mac(key *[]byte, parts ...[]byte) []byte {
23	m := hmac.New(sha256.New, *key)
24	for _, p := range parts {
25		m.Write(p)
26	}
27	return m.Sum(nil)
28}
29
30func seal(key *[]byte, payload []byte) string {
31	enc := base64.RawURLEncoding
32	return enc.EncodeToString(payload) + "." + enc.EncodeToString(mac(key, payload))
33}
34
35func unseal(key *[]byte, v string) ([]byte, error) {
36	p64, t64, ok := strings.Cut(v, ".")
37	if !ok {
38		return nil, errSeal
39	}
40	enc := base64.RawURLEncoding.Strict()
41	payload, err1 := enc.DecodeString(p64)
42	tag, err2 := enc.DecodeString(t64)
43	if err1 != nil || err2 != nil || !hmac.Equal(mac(key, payload), tag) {
44		return nil, errSeal
45	}
46	return payload, nil
47}