-
Notifications
You must be signed in to change notification settings - Fork 3
/
nonce.go
149 lines (121 loc) · 3.03 KB
/
nonce.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package macaroon
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
msgpack "github.com/vmihailenco/msgpack/v5"
)
const nonceRndSize = 16
type nonceV0Fields struct {
KID []byte `json:"kid"`
Rnd []byte `json:"rnd"`
}
type nonceV1Fields struct {
Proof bool `json:"proof"`
}
// A Nonce in cryptography is a random number that is only used
// once. A Nonce on a [Macaroon] is a blob of data that encodes,
// most impotantly, the "key ID" (KID) of the token; the KID is an
// opaque value that you, the library caller, provide when you create
// a token; it's the database key you use to tie the Macaroon to your
// database.
type Nonce struct {
nonceV0Fields
nonceV1Fields
version int
}
var (
_ msgpack.CustomDecoder = new(Nonce)
_ msgpack.CustomEncoder = new(Nonce)
)
const (
nonceV0 = iota
nonceV1
nonceVInvalid // keep this at end
)
var kidNamespace = uuid.MustParse("968fc2c6-a94f-4988-a544-2ad72b02f222")
// UUID is a simple globally unique identifier string for a nonce.
func (n Nonce) UUID() uuid.UUID {
kidUUID := uuid.NewSHA1(kidNamespace, n.KID)
rndUUID := uuid.NewSHA1(kidUUID, n.Rnd)
return rndUUID
}
// DecodeMsgpack implements [msgpack.CustomDecoder]
func (n *Nonce) DecodeMsgpack(d *msgpack.Decoder) error {
// we encode structs as arrays, so adding new fields is tricky...
// The Closed field was a later addition, so we handle 2 or 3 fields.
nFields, err := d.DecodeArrayLen()
if err != nil {
return err
}
switch nFields {
case 2:
n.version = nonceV0
case 3:
n.version = nonceV1
default:
return fmt.Errorf("unknown nonce format: %d fields", nFields)
}
if n.version >= nonceV0 {
if err = d.DecodeMulti(&n.nonceV0Fields.KID, &n.nonceV0Fields.Rnd); err != nil {
return err
}
}
if n.version >= nonceV1 {
if err = d.DecodeMulti(&n.Proof); err != nil {
return err
}
}
return nil
}
// EncodeMsgpack implements [msgpack.CustomDecoder]
func (n *Nonce) EncodeMsgpack(e *msgpack.Encoder) error {
var fields []any
if n.version >= 0 {
fields = append(fields, n.KID, n.Rnd)
}
if n.version >= 1 {
fields = append(fields, n.Proof)
}
return e.Encode(fields)
}
func (n Nonce) MustEncode() []byte {
b, err := encode(&n)
// this can only fail if writing to the buffer fails.
// it's convenienct to have a function that returns
// a single value.
if err != nil {
panic(err)
}
return b
}
func (n Nonce) MarshalJSON() ([]byte, error) {
raw, err := msgpack.Marshal(&n)
if err != nil {
return nil, err
}
return json.Marshal(raw)
}
func (c *Nonce) UnmarshalJSON(b []byte) error {
var raw []byte
if err := json.Unmarshal(b, &raw); err != nil {
return err
}
return msgpack.Unmarshal(raw, c)
}
// newNonce creates a nonce from a key-id, where the key-id
// is any opaque string; the resulting nonce has a bunch of random
// goo in it to goo up the nonce. A sane key-id might be a user-id
// or org-id.
func newNonce(kid []byte, isProof bool) Nonce {
return Nonce{
nonceV0Fields{
KID: kid,
Rnd: rbuf(nonceRndSize),
},
nonceV1Fields{
Proof: isProof,
},
nonceVInvalid - 1,
}
}