-
Notifications
You must be signed in to change notification settings - Fork 24
/
rpc.go
473 lines (410 loc) · 15.4 KB
/
rpc.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
// Copyright (c) 2016 Company 0, LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// rpc contains all structures required by the ZK protocol.
//
// A ZK session has two discrete phases:
// 1. pre session phase, used to create accounts and obtain zkserver key
// 2. session phase, used for all other RPC commands
// 3. once the key exchange is complete the server shall issue a Welcome
// command. The welcome command also transfer additional settings such
// as tag depth etc.
//
// In order to exchange messages with a third party two pieces of information
// are required. Each side must know the other's long lived public identity
// and the public DH ratchet keys.
// The process, using RPC, to obtains that information is as follows:
// 1. Alice sends Bob a Rendezvous command that contains her encrypted
// identity. She uses a third party communication method (phone, IRC
// etc) to share the rendezvous PIN code and a shared password.
// 2. Bob obtains Alice's identity by sending a RendezvousPull command
// using the PIN code. After decrypting Alice's identity blob using the
// share password he replies with a Cache command that contains his long
// lived public identity and his initial public DH ratchet keys.
// 3. Alice is notified, using the normal Push RPC mechanism, when Bob has
// replied. She then replies to Bob with her public DH ratchet keys.
//
// The external identity and key exchange process is outside of the scope of
// this document.
package rpc
import (
"crypto/sha256"
"errors"
"strconv"
"github.com/companyzero/zkc/ratchet"
"github.com/companyzero/zkc/zkidentity"
)
type MessageMode uint32
const (
// pre session phase
InitialCmdIdentify = "identify"
InitialCmdCreateAccount = "createaccount"
InitialCmdSession = "session"
// session phase
SessionCmdWelcome = "welcome"
SessionCmdUnwelcome = "unwelcome"
// tagged server commands
TaggedCmdRendezvous = "rendezvous"
TaggedCmdRendezvousReply = "rendezvousreply"
TaggedCmdRendezvousPull = "rendezvouspull"
TaggedCmdRendezvousPullReply = "rendezvouspullreply"
TaggedCmdCache = "cache"
TaggedCmdPush = "push"
TaggedCmdAcknowledge = "ack"
TaggedCmdProxy = "proxy"
TaggedCmdProxyReply = "proxyreply"
TaggedCmdPing = "ping"
TaggedCmdPong = "pong"
TaggedCmdIdentityFind = "identityfind"
TaggedCmdIdentityFindReply = "identityfindreply"
// misc
MessageModeNormal MessageMode = 0
MessageModeMe MessageMode = 1
ErrorCodeInvalid = 0 // invalid error code
ErrorCodeUserDisabled = 1 // user disabled
)
// CreateAccount is a PRPC that is used to create a new account on the server.
// Policy dictates if this is allowed or not.
type CreateAccount struct {
Token string // auth token
PublicIdentity zkidentity.PublicIdentity // long lived public identity
}
// sanitized errors for CreateAccountReply
var (
ErrCreateDisallowed = errors.New("not allowed")
ErrInternalError = errors.New("internal error, contact administrator")
)
// Message is the generic command that flows between a server and client and
// vice versa. Its purpose is to add a discriminator to simplify payload
// decoding. Additionally it has a tag that the recipient shall return
// unmodified when replying. The tag is originated by the sender and shall be
// unique provided an answer is expected. The receiver shall not interpret or
// use the tag in any way.
// The Cleartext flag indicates that the payload is in clear text. This flag
// should only be used for proxy commands (e.g. ratchet reset).
type Message struct {
Command string // discriminator
TimeStamp int64 // originator timestamp
Cleartext bool // If set Payload is in clear text, proxy use only
Tag uint32 // client generated tag, shall be unique
//followed by Payload []byte
}
// Acknowledge is sent to acknowledge commands and Error is set if the command
// failed.
type Acknowledge struct {
Error string
ErrorCode int // optional error to be used as a hint
}
const (
ProtocolVersion = 9
)
// Unwelcome is written immediately following a key exchange. This command
// purpose is to detect if the key exchange completed on the client side. If
// the key exchange failed the server will simply disconnect. If the user is
// Unwelcome this message will contain the reason.
type Unwelcome struct {
Version int // protocol version
Reason string // reason why unwelcome
}
// Welcome is written immediately following a key exchange. This command
// purpose is to detect if the key exchange completed on the client side. If
// the key exchange failed the server will simply disconnect.
type Welcome struct {
Version int // protocol version
ServerTime int64 // server timestamp
// Client shall ensure it is compatible with the server requirements
Properties []ServerProperty // server properties
}
type ServerProperty struct {
Key string // name of property
Value string // value of property
Required bool // if true client must handle this entry
}
const (
// Tag Depth is a required property. It defines maximum outstanding
// commands.
PropTagDepth = "tagdepth"
PropTagDepthDefault = "10"
// MOTD (Message Of The Day) is an optional property. It is a welcome
// message that is sent from the server to the client upon first
// contact. The client may display this.
PropMOTD = "motd"
// Max Attachment Size is a required property. It defines the maximum
// attachment size. Attachment size is defined as the largest size a
// file transfer is allowed to be.
PropMaxAttachmentSize = "maxattachmentsize"
PropMaxAttachmentSizeDefault = uint64(10 * 1024 * 1024)
// Max Chunk Size is a required property. It defines the maximum chunk
// size. Chunk size is defined as the largest size a CRPC is allowed
// to be.
PropMaxChunkSize = "maxchunksize"
PropMaxChunkSizeDefault = uint64(256 * 1024)
// Max Message Size is a required property. It defines the maximum
// message size. Message size is defined as the largest size a CRPC is
// allowed to be. This includes message overhead etc.
PropMaxMsgSize = "maxmsgsize"
PropMaxMsgSizeDefault = PropMaxChunkSizeDefault + 1024
// Server Time is a required property. It contains the server time
// stamp. The client shall warn the user if the client is not time
// synced. Clients and proxies really shall run NTP.
PropServerTime = "servertime"
// Directory is a required property. It defines whether the server
// keeps a directory of identities.
PropDirectory = "directory"
PropDirectoryDefault = false
)
var (
// required
DefaultPropTagDepth = ServerProperty{
Key: PropTagDepth,
Value: PropTagDepthDefault,
Required: true,
}
DefaultPropMaxAttachmentSize = ServerProperty{
Key: PropMaxAttachmentSize,
Value: strconv.FormatUint(PropMaxAttachmentSizeDefault, 10),
Required: true,
}
DefaultPropMaxChunkSize = ServerProperty{
Key: PropMaxChunkSize,
Value: strconv.FormatUint(PropMaxChunkSizeDefault, 10),
Required: true,
}
DefaultPropMaxMsgSize = ServerProperty{
Key: PropMaxMsgSize,
Value: strconv.FormatUint(PropMaxMsgSizeDefault, 10),
Required: true,
}
DefaultServerTime = ServerProperty{
Key: PropServerTime,
Value: "", // int64 unix time
Required: true,
}
DefaultPropDirectory = ServerProperty{
Key: PropDirectory,
Value: strconv.FormatBool(PropDirectoryDefault),
Required: true,
}
// optional
DefaultPropMOTD = ServerProperty{
Key: PropMOTD,
Value: "",
Required: false,
}
// All properties must exist in this array.
SupportedServerProperties = []ServerProperty{
// required
DefaultPropTagDepth,
DefaultPropMaxAttachmentSize,
DefaultPropMaxChunkSize,
DefaultPropMaxMsgSize,
DefaultServerTime,
DefaultPropDirectory,
// optional
DefaultPropMOTD,
}
)
// CreateAccountReply returns a sanitized error to the client indicating
// success or failure of the CreateAccountReply command. Errors is set to ""
// on success.
type CreateAccountReply struct {
Error string // if create account failed error contains the reason.
}
// Push is a PRPC that is used to push cached encrypted blobs to a user. This
// command must be acknowledged by the remote side.
type Push struct {
From [32]byte // sender identity
Received int64 // server received timestamp
Payload []byte // encrypted payload
}
// Cache is a PRPC that is used to store message on server for later push
// delivery. This command must be acknowledged by the remote side.
type Cache struct {
To [32]byte // recipient identity
Payload []byte // encrypted payload
}
// Proxy is a PRPC that is used to store message on server for later push
// delivery. This command must be acknowledged by the remote side.
// THIS COMMAND IS NOT ENCRYPTED AND IS ONLY TO BE USED DURING EMERGENCIES
// (like a ratchet reset).
type Proxy struct {
To [32]byte // recipient identity
Payload []byte // unencrypted payload
}
// ProxyReply returns with an Error set if an error occurred during delivery.
type ProxyReply struct {
To [32]byte // recipient identity, returned by server
Error string // Set if an error occurred
}
// All proxy commands are a uint32 followed by a string. We do this to make
// decoding easier and since these are emergency commands nothing more should
// be sent anyway.
const (
ProxyCmdInvalid = uint32(0)
ProxyCmdResetRatchet = uint32(1)
)
// ProxyCmd is sent in clear text from one client to another.
type ProxyCmd struct {
Command uint32 // Command type
Message string // message from other client
}
// Ping is a PRPC that is used to determine if the server is alive.
// This command must be acknowledged by the remote side.
type Ping struct{}
type Pong struct{}
// client to client commands
// Rendezvous sends a blob to the server. Blob shall be < 4096 and
// expiration shall be < 168 (7 * 24).
type Rendezvous struct {
Blob []byte // data being shared
Expiration string // hours until Rendezvous expires
}
// RendezvousReply is a reply packet for a Rendezvous command. Token contains
// an easy to remember PIN code to identify initial Rendezvous blob.
type RendezvousReply struct {
Token string // Rendezvous token that identifies blob
Error string // If an error occurred Error will be != ""
}
// RendezvousPull tries to download a previously uploaded blob.
type RendezvousPull struct {
Token string // Rendezvous token that identifies blob
}
// RendezvousPullReply contains a data blob reply to a previous RendezvousPull
// command that is identified by token.
type RendezvousPullReply struct {
Error string // set if an error occurred
Token string // Rendezvous token that identifies blob
Blob []byte // data reply to previous Rendezvous
}
// IdentityFind asks the server's directory if the provided bick exists. The
// server will always return a failure if the nick is not found or if directory
// services are not enabled.
type IdentityFind struct {
Nick string
}
// IdentityFindReply contains a public identity if found.
type IdentityFindReply struct {
Nick string // Nick that was originally sent in
Error string // Set if an error occurred
Identity zkidentity.PublicIdentity // Public Identify if Error not set
}
// IdentityKX contains the long lived public identify and the DH ratchet keys.
// It is the second step during the IDKX exchange.
type IdentityKX struct {
Identity zkidentity.PublicIdentity
KX ratchet.KeyExchange
}
// KX contains the DH ratchet keys. It is the third step during the IDKX
// exchange.
type KX struct {
KX ratchet.KeyExchange
}
const (
// CRPC commands
CRPCCmdPrivateMessage = "privmsg"
CRPCCmdGroupInvite = "groupinvite"
CRPCCmdGroupJoin = "groupjoin"
CRPCCmdGroupPart = "grouppart"
CRPCCmdGroupKill = "groupkill"
CRPCCmdGroupKick = "groupkick"
CRPCCmdGroupUpdate = "groupupdate"
CRPCCmdGroupList = "grouplist"
CRPCCmdGroupMessage = "groupmessage"
CRPCCmdChunkNew = "chunknew"
CRPCCmdChunk = "chunk"
CRPCCmdJanitorMessage = "janitormessage"
// compression
CRPCCompNone = ""
CRPCCompZLIB = "zlib"
// janitor
CRPCJanitorDeleted = "deleted"
)
// CRPC is a client RPC message.
type CRPC struct {
Timestamp int64 // client side timestamp
Command string // discriminator
Compression string // compression used on Payload
//followed by Payload []byte
}
// PrivateMessage is a CRPC that contains a text message.
type PrivateMessage struct {
Text string
Mode MessageMode // 0 regular mode, 1 /me
}
// JanitorMessage is a CRPC that tells the other party some sort of
// housekeeping occurred.
type JanitorMessage struct {
Command string
Reason string
}
// GroupInvite, sender is implicit to CRPC.
// XXX Note that there is no explicit way to prohibit sender being admin.
// XXX This needs some more thought.
type GroupInvite struct {
Name string // group name
Members []string // list of participants' nicknames
Token uint64 // invite token
Description string // group description
Expires int64 // unix time when this invite expires
}
// GroupJoin
type GroupJoin struct {
Name string // group name
Token uint64 // invite token, implicitly identifies sender
Error string // accept or deny Invite
}
// GroupPart, sender is implicit to CRPC
type GroupPart struct {
Name string // group name
Reason string // reason to depart group
}
// GroupKill, sender is implicit to CRPC
type GroupKill struct {
Name string // group name
Reason string // reason to disassemble group
}
// GroupKick, sender is implicit to CRPC
type GroupKick struct {
Member [zkidentity.IdentitySize]byte // kickee
Reason string // why member was kicked
Parted bool // kicked/parted
NewGroupList GroupList // new GroupList
}
// GroupUpdate is a forced update from the admin. Thi can be used in case of
// gc' generation getting out of sync.
type GroupUpdate struct {
Reason string // why member was kicked
NewGroupList GroupList // new GroupList
}
// GroupList, currently we detect spoofing by ensuring the origin of the
// message. This may not be sufficient and we may have to add a signature of
// sorts. For now roll with this assumption.
type GroupList struct {
Name string // group name
Generation uint64 // incremented every time list changes
Timestamp int64 // unix time last generation changed
// all participants, [0] is administrator
// receiver must check [0] == originator
Members [][zkidentity.IdentitySize]byte
}
// GroupMessage is a message to a group.
type GroupMessage struct {
Name string // group name
Generation uint64 // Generation used
Message string // Actual message
Mode MessageMode // 0 regular mode, 1 /me
}
// ChunkNew describes a chunked file transfer initiation.
type ChunkNew struct {
Size uint64 // total file size
ChunkSize uint64 // chunk size
Filename string // original filename
Description string // user provided description
MIME string // mime type
Digest [sha256.Size]byte // digest of file -> unique identifier
}
type Chunk struct {
Offset uint64 // offset in file
Digest [sha256.Size]byte // digest of file -> unique identifier
Payload []byte // chunk
}