/
codec.go
219 lines (198 loc) · 6.7 KB
/
codec.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package bakery
import (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"encoding/json"
"golang.org/x/crypto/nacl/box"
"gopkg.in/errgo.v1"
)
type caveatInfo struct {
peerPublicKey *PublicKey
rootKey []byte
condition string
}
type caveatIdRecord struct {
RootKey []byte
Condition string
}
// caveatId defines the format of a third party caveat id.
type caveatId struct {
ThirdPartyPublicKey *PublicKey
FirstPartyPublicKey *PublicKey
Nonce []byte
Id string
}
// encodeJSONCaveatId creates a JSON encoded third-party caveat.
func encodeJSONCaveatId(key *KeyPair, ci caveatInfo) ([]byte, error) {
var nonce [NonceLen]byte
if _, err := rand.Read(nonce[:]); err != nil {
return nil, errgo.Notef(err, "cannot generate random number for nonce")
}
plain := caveatIdRecord{
RootKey: ci.rootKey,
Condition: ci.condition,
}
plainData, err := json.Marshal(&plain)
if err != nil {
return nil, errgo.Notef(err, "cannot marshal %#v", &plain)
}
sealed := box.Seal(nil, plainData, &nonce, ci.peerPublicKey.boxKey(), key.Private.boxKey())
id := caveatId{
ThirdPartyPublicKey: ci.peerPublicKey,
FirstPartyPublicKey: &key.Public,
Nonce: nonce[:],
Id: base64.StdEncoding.EncodeToString(sealed),
}
data, err := json.Marshal(id)
if err != nil {
return nil, errgo.Notef(err, "cannot marshal %#v", id)
}
buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(buf, data)
return buf, nil
}
const (
publicKeyPrefixLen = 4
)
// encodeCaveatIdV0 creates a version 0 third-party caveat.
//
// The v0 format has the following packed binary fields:
// version 0 [1 byte]
// first 4 bytes of third-party Curve25519 public key [4 bytes]
// first-party Curve25519 public key [32 bytes]
// nonce [24 bytes]
// encrypted secret part [rest of message]
func encodeCaveatIdV0(key *KeyPair, ci caveatInfo) ([]byte, error) {
var nonce [NonceLen]byte
if _, err := rand.Read(nonce[:]); err != nil {
return nil, errgo.Notef(err, "cannot generate random number for nonce")
}
data := make([]byte, 0, 1+publicKeyPrefixLen+KeyLen+NonceLen+1+binary.MaxVarintLen64+len(ci.rootKey)+len(ci.condition)+box.Overhead)
data = append(data, 0) //version
data = append(data, ci.peerPublicKey.Key[:publicKeyPrefixLen]...)
data = append(data, key.Public.Key[:]...)
data = append(data, nonce[:]...)
data = box.Seal(data, encodeSecretPartV0(ci), &nonce, ci.peerPublicKey.boxKey(), key.Private.boxKey())
return data, nil
}
// encodeSecretPartV0 creates a version 0 secret part of the third party
// caveat. The generated secret part is not encrypted.
//
// The v0 format has the following packed binary fields:
// version 0 [1 byte]
// root key [24 bytes]
// predicate [rest of message]
func encodeSecretPartV0(ci caveatInfo) []byte {
data := make([]byte, 0, 1+binary.MaxVarintLen64+len(ci.rootKey)+len(ci.condition))
data = append(data, 0) // version
n := binary.PutUvarint(data[1:1+binary.MaxVarintLen64], uint64(len(ci.rootKey)))
data = data[0 : len(data)+n]
data = append(data, ci.rootKey...)
data = append(data, ci.condition...)
return data
}
// decodeCaveatId attempts to decode id decrypting the encrypted part
// using key.
func decodeCaveatId(key *KeyPair, id []byte) (caveatInfo, error) {
if len(id) == 0 {
return caveatInfo{}, errgo.New("caveat id empty")
}
switch id[0] {
case 0:
return decodeCaveatIdV0(key, []byte(id))
case 'e':
// 'e' will be the first byte if the caveatid is a base64 encoded JSON object.
return decodeJSONCaveatId(key, id)
default:
return caveatInfo{}, errgo.Newf("caveat id has unsupported version %d", id[0])
}
}
// decodeJSONCaveatId attempts to decode a base64 encoded JSON id. This
// encoding is nominally version -1.
func decodeJSONCaveatId(key *KeyPair, id []byte) (caveatInfo, error) {
data := make([]byte, (3*len(id)+3)/4)
n, err := base64.StdEncoding.Decode(data, id)
if err != nil {
return caveatInfo{}, errgo.Notef(err, "cannot base64-decode caveat id")
}
data = data[:n]
var tpid caveatId
if err := json.Unmarshal(data, &tpid); err != nil {
return caveatInfo{}, errgo.Notef(err, "cannot unmarshal caveat id %q", data)
}
if !bytes.Equal(key.Public.Key[:], tpid.ThirdPartyPublicKey.Key[:]) {
return caveatInfo{}, errgo.New("public key mismatch")
}
if tpid.FirstPartyPublicKey == nil {
return caveatInfo{}, errgo.New("target service public key not specified")
}
// The encrypted string is base64 encoded in the JSON representation.
secret, err := base64.StdEncoding.DecodeString(tpid.Id)
if err != nil {
return caveatInfo{}, errgo.Notef(err, "cannot base64-decode encrypted data")
}
var nonce [NonceLen]byte
if copy(nonce[:], tpid.Nonce) < NonceLen {
return caveatInfo{}, errgo.Newf("nonce too short %x", tpid.Nonce)
}
cid, ok := box.Open(nil, secret, &nonce, tpid.FirstPartyPublicKey.boxKey(), key.Private.boxKey())
if !ok {
return caveatInfo{}, errgo.Newf("cannot decrypt caveat id %#v", tpid)
}
var record caveatIdRecord
if err := json.Unmarshal(cid, &record); err != nil {
return caveatInfo{}, errgo.Notef(err, "cannot decode third party caveat record")
}
return caveatInfo{
peerPublicKey: tpid.FirstPartyPublicKey,
rootKey: record.RootKey,
condition: record.Condition,
}, nil
}
// decodeCaveatIdV0 decodes a version 0 caveat id.
func decodeCaveatIdV0(key *KeyPair, id []byte) (caveatInfo, error) {
if len(id) < 1+publicKeyPrefixLen+KeyLen+NonceLen+box.Overhead {
return caveatInfo{}, errgo.New("caveat id too short")
}
id = id[1:] // skip version (already checked)
publicKeyPrefix, id := id[:publicKeyPrefixLen], id[publicKeyPrefixLen:]
if !bytes.Equal(key.Public.Key[:publicKeyPrefixLen], publicKeyPrefix) {
return caveatInfo{}, errgo.New("public key mismatch")
}
var peerPublicKey PublicKey
copy(peerPublicKey.Key[:], id[:KeyLen])
id = id[KeyLen:]
var nonce [NonceLen]byte
copy(nonce[:], id[:NonceLen])
id = id[NonceLen:]
data, ok := box.Open(nil, id, &nonce, peerPublicKey.boxKey(), key.Private.boxKey())
if !ok {
return caveatInfo{}, errgo.Newf("cannot decrypt caveat id")
}
ci, err := decodeSecretPartV0(data)
if err != nil {
return caveatInfo{}, errgo.Notef(err, "invalid secret part")
}
ci.peerPublicKey = &peerPublicKey
return ci, nil
}
func decodeSecretPartV0(data []byte) (caveatInfo, error) {
if len(data) < 1 {
return caveatInfo{}, errgo.New("secret part too short")
}
version, data := data[0], data[1:]
if version != 0 {
return caveatInfo{}, errgo.Newf("unsupported secret part version %d", version)
}
l, n := binary.Uvarint(data)
if n <= 0 || uint64(n)+l > uint64(len(data)) {
return caveatInfo{}, errgo.Newf("invalid root key length")
}
data = data[n:]
return caveatInfo{
rootKey: data[:l],
condition: string(data[l:]),
}, nil
}