-
Notifications
You must be signed in to change notification settings - Fork 18
/
messagetypes.go
627 lines (507 loc) · 17.6 KB
/
messagetypes.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
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
package o3
import (
"fmt"
"io/ioutil"
mrand "math/rand"
"time"
"errors"
)
// MsgType determines the type of message that is sent or received. Users usually
// won't use this directly and rather use message generator functions.
type MsgType uint8
// MsgType mock enum
const (
TEXTMESSAGE MsgType = 0x1 //indicates a text message
IMAGEMESSAGE MsgType = 0x2 //indicates a image message
AUDIOMESSAGE MsgType = 0x14 //indicates a audio message
POLLMESSAGE MsgType = 0x15 //indicates a poll message
LOCATIONMESSAGE MsgType = 0x16 //indicates a location message
FILEMESSAGE MsgType = 0x17 //indicates a file message
GROUPTEXTMESSAGE MsgType = 0x41 //indicates a group text message
GROUPIMAGEMESSAGE MsgType = 0x43 //indicates a group image message
GROUPSETMEMEBERSMESSAGE MsgType = 0x4A //indicates a set group member message
GROUPSETNAMEMESSAGE MsgType = 0x4B //indicates a set group name message
GROUPMEMBERLEFTMESSAGE MsgType = 0x4C //indicates a group member left message
GROUPSETIMAGEMESSAGE MsgType = 0x50 //indicates a group set image message
DELIVERYRECEIPT MsgType = 0x80 //indicates a delivery receipt sent by the threema servers
TYPINGNOTIFICATION MsgType = 0x90 //indicates a typing notifiaction message
//GROUPSETIMAGEMESSAGE msgType = 76
)
// MsgStatus represents the single-byte status field of DeliveryReceiptMessage
type MsgStatus uint8
//MsgStatus mock enum
const (
MSGDELIVERED MsgStatus = 0x1 //indicates message was received by peer
MSGREAD MsgStatus = 0x2 //indicates message was read by peer
MSGAPPROVED MsgStatus = 0x3 //indicates message was approved (thumb up) by peer
MSGDISAPPROVED MsgStatus = 0x4 //indicates message was disapproved (thumb down) by peer
)
//TODO: figure these out
type msgFlags struct {
PushMessage bool
NoQueuing bool
NoAckExpected bool
MessageHasAlreadyBeenDelivered bool
GroupMessage bool
}
// NewMsgID returns a randomly generated message ID (not cryptographically secure!)
// TODO: Why mrand?
func NewMsgID() uint64 {
mrand.Seed(int64(time.Now().Nanosecond()))
msgID := uint64(mrand.Int63())
return msgID
}
// NewGrpID returns a randomly generated group ID (not cryptographically secure!)
// TODO: Why mrand?
func NewGrpID() [8]byte {
mrand.Seed(int64(time.Now().Nanosecond()))
grpIDbuf := make([]byte, 8)
mrand.Read(grpIDbuf)
var grpID [8]byte
copy(grpID[:], grpIDbuf)
return grpID
}
// Message representing the various kinds of e2e ecrypted messages threema supports
type Message interface {
//Sender returns the message's sender ID
Sender() IDString
//Serialize returns a fully serialized byte slice of the message
Serialize() []byte
header() messageHeader
}
type messageHeader struct {
sender IDString
recipient IDString
id uint64
time time.Time
pubNick PubNick
}
func (mh messageHeader) Sender() IDString {
return mh.sender
}
func (mh messageHeader) Recipient() IDString {
return mh.recipient
}
func (mh messageHeader) ID() uint64 {
return mh.id
}
func (mh messageHeader) Time() time.Time {
return mh.time
}
func (mh messageHeader) PubNick() PubNick {
return mh.pubNick
}
//TODO: WAT?
func (mh messageHeader) header() messageHeader {
return mh
}
//--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
type textMessageBody struct {
text string
}
//TextMessage represents a text message as sent e2e encrypted to other threema users
type TextMessage struct {
messageHeader
textMessageBody
}
// NewTextMessage returns a TextMessage ready to be encrypted
func NewTextMessage(sc *SessionContext, recipient string, text string) (TextMessage, error) {
recipientID := NewIDString(recipient)
tm := TextMessage{
messageHeader{
sender: sc.ID.ID,
recipient: recipientID,
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick,
},
textMessageBody{text: text},
}
return tm, nil
}
// Text returns the message text
func (tm TextMessage) Text() string {
return tm.text
}
// String returns the message text as string
func (tm TextMessage) String() string {
return tm.Text()
}
//Serialize returns a fully serialized byte slice of a TextMessage
func (tm TextMessage) Serialize() []byte {
return serializeTextMsg(tm).Bytes()
}
//Serialize returns a fully serialized byte slice of a TypingNotificationMessage
func (tn TypingNotificationMessage) Serialize() []byte {
return serializeTypingNotification(tn).Bytes()
}
//--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
//ImageMessage represents an image message as sent e2e encrypted to other threema users
type ImageMessage struct {
messageHeader
imageMessageBody
}
type imageMessageBody struct {
BlobID [16]byte
ServerID byte
Size uint32
Nonce nonce
}
// NewImageMessage returns a ImageMessage ready to be encrypted
func NewImageMessage(sc *SessionContext, recipient string, filename string) (ImageMessage, error) {
recipientID := NewIDString(recipient)
im := ImageMessage{
messageHeader{
sender: sc.ID.ID,
recipient: recipientID,
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick,
},
imageMessageBody{},
}
err := im.SetImageData(filename, *sc)
if err != nil {
return ImageMessage{}, err
}
return im, nil
}
// GetPrintableContent returns a printable represantion of a ImageMessage.
func (im ImageMessage) GetPrintableContent() string {
return fmt.Sprintf("ImageMSG: https://%2x.blob.threema.ch/%16x, Size: %d, Nonce: %24x", im.ServerID, im.BlobID, im.Size, im.Nonce.nonce)
}
// GetImageData return the decrypted Image needs the recipients secret key
func (im ImageMessage) GetImageData(sc SessionContext) ([]byte, error) {
return downloadAndDecryptAsym(sc, im.BlobID, im.Sender().String(), im.Nonce)
}
// SetImageData encrypts and uploads the image. Sets the blob info in the ImageMessage. Needs the recipients public key.
func (im *ImageMessage) SetImageData(filename string, sc SessionContext) error {
plainImage, err := ioutil.ReadFile(filename)
if err != nil {
return errors.New("could not load image")
}
im.Nonce, im.ServerID, im.Size, im.BlobID, err = encryptAndUploadAsym(sc, plainImage, im.recipient.String())
return err
}
//Serialize returns a fully serialized byte slice of an ImageMessage
func (im ImageMessage) Serialize() []byte {
return serializeImageMsg(im).Bytes()
}
//--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
//AudioMessage represents an image message as sent e2e encrypted to other threema users
type AudioMessage struct {
messageHeader
audioMessageBody
}
type audioMessageBody struct {
Duration uint16 // The audio clips duration in seconds
BlobID [16]byte
ServerID byte
Size uint32
Key [32]byte
}
// NewAudioMessage returns a ImageMessage ready to be encrypted
func NewAudioMessage(sc *SessionContext, recipient string, filename string) (AudioMessage, error) {
recipientID := NewIDString(recipient)
im := AudioMessage{
messageHeader{
sender: sc.ID.ID,
recipient: recipientID,
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick,
},
audioMessageBody{},
}
err := im.SetAudioData(filename, *sc)
if err != nil {
return AudioMessage{}, err
}
return im, nil
}
// GetPrintableContent returns a printable represantion of an AudioMessage
func (am AudioMessage) GetPrintableContent() string {
return fmt.Sprintf("AudioMSG: https://%2x.blob.threema.ch/%16x, Size: %d, Nonce: %24x", am.ServerID, am.BlobID, am.Size, am.Key)
}
// GetAudioData return the decrypted audio, needs the recipients secret key
func (am AudioMessage) GetAudioData(sc SessionContext) ([]byte, error) {
return downloadAndDecryptSym(am.BlobID, am.Key)
}
// SetAudioData encrypts and uploads the audio. Sets the blob info in the ImageMessage. Needs the recipients public key.
func (am *AudioMessage) SetAudioData(filename string, sc SessionContext) error {
plainAudio, err := ioutil.ReadFile(filename)
if err != nil {
return errors.New("could not load audio")
}
// TODO: Should we have a whole media lib as dependency just to set this to the proper value?
am.Duration = 0xFF
am.Key, am.ServerID, am.Size, am.BlobID, err = encryptAndUploadSym(plainAudio)
return err
}
//Serialize returns a fully serialized byte slice of an AudioMessage
func (am AudioMessage) Serialize() []byte {
return serializeAudioMsg(am).Bytes()
}
//--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
//TypingNotificationMessage represents a typing notifiaction message
type TypingNotificationMessage struct {
messageHeader
typingNotificationBody
}
type typingNotificationBody struct {
OnOff byte
}
//--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
// NewGroupTextMessages returns a slice of GroupMemberTextMessages ready to be encrypted
func NewGroupTextMessages(sc *SessionContext, group Group, text string) ([]GroupTextMessage, error) {
gtm := make([]GroupTextMessage, len(group.Members))
var tm TextMessage
var err error
for i, member := range group.Members {
tm, err = NewTextMessage(sc, member.String(), text)
if err != nil {
return []GroupTextMessage{}, err
}
gtm[i] = GroupTextMessage{
groupMessageHeader{
creatorID: group.CreatorID,
groupID: group.GroupID},
tm}
}
return gtm, nil
}
//GroupTextMessage represents a group text message as sent e2e encrypted to other threema users
type GroupTextMessage struct {
groupMessageHeader
TextMessage
}
type groupMessageHeader struct {
creatorID IDString
groupID [8]byte
}
// Serialize : returns byte representation of serialized group text message
func (gtm GroupTextMessage) Serialize() []byte {
return serializeGroupTextMsg(gtm).Bytes()
}
type groupImageMessageBody struct {
BlobID [16]byte
ServerID byte
Size uint32
Key [32]byte
}
// GroupCreator returns the ID of the groups admin/creator as string
func (gmh groupMessageHeader) GroupCreator() IDString {
return gmh.creatorID
}
// GroupID returns the ID of the group the message belongs to
func (gmh groupMessageHeader) GroupID() [8]byte {
return gmh.groupID
}
//GroupImageMessage represents a group image message as sent e2e encrypted to other threema users
type GroupImageMessage struct {
groupMessageHeader
messageHeader
groupImageMessageBody
}
//Serialize returns a fully serialized byte slice of a GroupImageMessage
func (im GroupImageMessage) Serialize() []byte {
return serializeGroupImageMsg(im).Bytes()
}
// GetImageData return the decrypted Image needs the recipients secret key
func (im GroupImageMessage) GetImageData(sc SessionContext) ([]byte, error) {
return downloadAndDecryptSym(im.BlobID, im.Key)
}
// SetImageData encrypts the given image symmetrically and adds it to the message
func (im *GroupImageMessage) SetImageData(filename string) error {
return im.groupImageMessageBody.setImageData(filename)
}
func (im *groupImageMessageBody) setImageData(filename string) error {
plainImage, err := ioutil.ReadFile(filename)
if err != nil {
return errors.New("could not load image")
}
im.Key, im.ServerID, im.Size, im.BlobID, err = encryptAndUploadSym(plainImage)
return err
}
// NewGroupMemberLeftMessages returns a slice of GroupMemberLeftMessages ready to be encrypted
func NewGroupMemberLeftMessages(sc *SessionContext, group Group) []GroupMemberLeftMessage {
gml := make([]GroupMemberLeftMessage, len(group.Members))
for i := 0; i < len(group.Members); i++ {
gml[i] = GroupMemberLeftMessage{
groupMessageHeader{
creatorID: group.CreatorID,
groupID: group.GroupID},
messageHeader{
sender: sc.ID.ID,
recipient: group.Members[i],
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick}}
}
return gml
}
//Serialize returns a fully serialized byte slice of a GroupMemberLeftMessage
func (gml GroupMemberLeftMessage) Serialize() []byte {
return serializeGroupMemberLeftMessage(gml).Bytes()
}
//GroupMemberLeftMessage represents a group leaving message
type GroupMemberLeftMessage struct {
groupMessageHeader
messageHeader
}
// NewDeliveryReceiptMessage returns a TextMessage ready to be encrypted
func NewDeliveryReceiptMessage(sc *SessionContext, recipient string, msgID uint64, msgStatus MsgStatus) (DeliveryReceiptMessage, error) {
recipientID := NewIDString(recipient)
dm := DeliveryReceiptMessage{
messageHeader{
sender: sc.ID.ID,
recipient: recipientID,
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick,
},
deliveryReceiptMessageBody{
msgID: msgID,
status: msgStatus},
}
return dm, nil
}
type deliveryReceiptMessageBody struct {
status MsgStatus
msgID uint64
}
// DeliveryReceiptMessage represents a delivery receipt as sent e2e encrypted to other threema users when a message has been received
type DeliveryReceiptMessage struct {
messageHeader
deliveryReceiptMessageBody
}
// GetPrintableContent returns a printable represantion of a DeliveryReceiptMessage.
func (dm DeliveryReceiptMessage) GetPrintableContent() string {
return fmt.Sprintf("Delivered: %x", dm.msgID)
}
//Serialize returns a fully serialized byte slice of a SeliveryReceiptMessage
func (dm DeliveryReceiptMessage) Serialize() []byte {
return serializeDeliveryReceiptMsg(dm).Bytes()
}
// Status returns the messages status
func (dm DeliveryReceiptMessage) Status() MsgStatus {
return dm.status
}
// MsgID returns the message id
func (dm DeliveryReceiptMessage) MsgID() uint64 {
return dm.msgID
}
// GROUP MANAGEMENT MESSAGES
////////////////////////////////////////////////////////////////
// TODO: Implement message interface
type groupManageMessageHeader struct {
groupID [8]byte
}
func (gmh groupManageMessageHeader) GroupID() [8]byte {
return gmh.groupID
}
// NewGroupManageSetMembersMessages returns a slice of GroupManageSetMembersMessages ready to be encrypted
func NewGroupManageSetMembersMessages(sc *SessionContext, group Group) []GroupManageSetMembersMessage {
gms := make([]GroupManageSetMembersMessage, len(group.Members))
for i := 0; i < len(group.Members); i++ {
gms[i] = GroupManageSetMembersMessage{
groupManageMessageHeader{
groupID: group.GroupID},
messageHeader{
sender: sc.ID.ID,
recipient: group.Members[i],
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick},
groupManageSetMembersMessageBody{
groupMembers: group.Members}}
}
return gms
}
type groupManageSetMembersMessageBody struct {
groupMembers []IDString
}
// GroupManageSetImageMessage represents the message sent e2e-encrypted by a group's creator to all members to set the group image
type GroupManageSetImageMessage struct {
groupManageMessageHeader
messageHeader
groupImageMessageBody
}
// NewGroupManageSetImageMessages returns a slice of GroupManageSetImageMessages ready to be encrypted
func NewGroupManageSetImageMessages(sc *SessionContext, group Group, filename string) []GroupManageSetImageMessage {
gms := make([]GroupManageSetImageMessage, len(group.Members))
for i := 0; i < len(group.Members); i++ {
gms[i] = GroupManageSetImageMessage{
groupManageMessageHeader{
groupID: group.GroupID},
messageHeader{}, //TODO:
groupImageMessageBody{},
}
err := gms[i].SetImageData(filename)
if err != nil {
//TODO: pretty sure this isn't a good idea
return nil
}
}
return gms
}
// GetImageData returns the decrypted Image
func (im GroupManageSetImageMessage) GetImageData(sc SessionContext) ([]byte, error) {
return downloadAndDecryptSym(im.BlobID, im.Key)
}
// SetImageData encrypts the given image symmetrically and adds it to the message
func (im *GroupManageSetImageMessage) SetImageData(filename string) error {
return im.groupImageMessageBody.setImageData(filename)
}
//Serialize returns a fully serialized byte slice of an ImageMessage
func (im GroupManageSetImageMessage) Serialize() []byte {
return serializeGroupManageSetImageMessage(im).Bytes()
}
// GroupManageSetMembersMessage represents the message sent e2e encrypted by a group's creator to all members
type GroupManageSetMembersMessage struct {
groupManageMessageHeader
messageHeader
groupManageSetMembersMessageBody
}
//Members returns a byte slice of IDString of all members contained in the message
func (gmm GroupManageSetMembersMessage) Members() []IDString {
return gmm.groupMembers
}
//Serialize returns a fully serialized byte slice of a GroupManageSetMembersMessage
func (gmm GroupManageSetMembersMessage) Serialize() []byte {
return serializeGroupManageSetMembersMessage(gmm).Bytes()
}
// NewGroupManageSetNameMessages returns a slice of GroupMenageSetNameMessages ready to be encrypted
func NewGroupManageSetNameMessages(sc *SessionContext, group Group) []GroupManageSetNameMessage {
gms := make([]GroupManageSetNameMessage, len(group.Members))
for i := 0; i < len(group.Members); i++ {
gms[i] = GroupManageSetNameMessage{
groupManageMessageHeader{
groupID: group.GroupID},
messageHeader{
sender: sc.ID.ID,
recipient: group.Members[i],
id: NewMsgID(),
time: time.Now(),
pubNick: sc.ID.Nick},
groupManageSetNameMessageBody{
groupName: group.Name}}
}
return gms
}
type groupManageSetNameMessageBody struct {
groupName string
}
func (gmm groupManageSetNameMessageBody) Name() string {
return gmm.groupName
}
//Serialize returns a fully serialized byte slice of a GroupManageSetNameMessage
func (gmm GroupManageSetNameMessage) Serialize() []byte {
return serializeGroupManageSetNameMessage(gmm).Bytes()
}
//GroupManageSetNameMessage represents a group management messate to set the group name
type GroupManageSetNameMessage struct {
groupManageMessageHeader
messageHeader
groupManageSetNameMessageBody
}