Skip to content

Commit

Permalink
add image+audio support + refactory group
Browse files Browse the repository at this point in the history
  • Loading branch information
genofire committed Sep 9, 2019
1 parent ebd3b6c commit 3b942f8
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 95 deletions.
6 changes: 2 additions & 4 deletions blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ func uploadBlob(blob []byte) ([16]byte, error) {
}

// encryptAsymAndUpload encrypts a blob with recipients PK and the sc owners SK
func encryptAsymAndUpload(sc SessionContext, plainImage []byte, recipientName string) (blobNonce nonce, ServerID byte, size uint32, blobID [16]byte, err error) {
func encryptAsymAndUpload(threemaID *ThreemaID, plainImage []byte, recipientName string) (blobNonce nonce, ServerID byte, size uint32, blobID [16]byte, err error) {
// Get contact public key
threemaID := sc.ID
recipient, inContacts := threemaID.Contacts.Get(recipientName)
if !inContacts {
var tr ThreemaRest
Expand Down Expand Up @@ -157,14 +156,13 @@ func downloadBlob(blobID [16]byte) ([]byte, error) {
return ciphertext, nil
}

func downloadAndDecryptAsym(sc SessionContext, blobID [16]byte, senderName string, blobNonce nonce) (plaintext []byte, err error) {
func downloadAndDecryptAsym(threemaID *ThreemaID, blobID [16]byte, senderName string, blobNonce nonce) (plaintext []byte, err error) {
ciphertext, err := downloadBlob(blobID)
if err != nil {
return []byte{}, err
}

var sender ThreemaContact
threemaID := sc.ID
sender, inContacts := threemaID.Contacts.Get(senderName)
if !inContacts {
var tr ThreemaRest
Expand Down
92 changes: 92 additions & 0 deletions message_audio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package o3

import (
"bytes"
"errors"
"fmt"
"io/ioutil"
)

// MsgType mock enum
const MessageTypeAudio MsgType = 0x14

//AudioMessage represents a image message as sent e2e encrypted to other threema users
type AudioMessage struct {
*MessageHeader
Duration uint16
BlobID [16]byte
ServerID byte
Size uint32
Key [32]byte
}

// String returns the message text as string
func (m AudioMessage) String() string {
return fmt.Sprintf("AudioMSG: https://%2x.blob.threema.ch/%16x, Size: %d, Key: %24x", m.ServerID, m.BlobID, m.Size, m.Key)
}

// GetData return the decrypted Audio needs the recipients secret key
func (m AudioMessage) GetData() ([]byte, error) {
return downloadAndDecryptSym(m.BlobID, m.Key)
}

// SetAudio encrypts and uploads the image by file. Sets the blob info in the AudioMessage. Needs the recipients public key.
func (m *AudioMessage) SetDataByFile(filename string) error {
data, err := ioutil.ReadFile(filename)
if err != nil {
return errors.New("could not load image")
}
return m.SetData(data)
}

// SetAudioData encrypts and uploads the image. Sets the blob info in the AudioMessage. Needs the recipients public key.
func (m *AudioMessage) SetData(data []byte) (err error) {
// TODO: Should we have a whole media lib as dependency just to set this to the proper value?
m.Duration = 0xFF
m.Key, m.ServerID, m.Size, m.BlobID, err = encryptSymAndUpload(data)
return
}

//Serialize returns a fully serialized byte slice of a TextMessage
func (m AudioMessage) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
bufMarshal("msg-type", buf, MessageTypeAudio)
bufMarshal("duration", buf, 0xFFFF)
bufMarshal("blob-id", buf, m.BlobID)
bufMarshal("size", buf, m.Size)
bufMarshal("nonce", buf, m.Key)
bufMarshalPadding(buf)

return buf.Bytes(), nil
}

func (m *AudioMessage) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
var t MsgType
bufUnmarshal("read message type", buf, &t)
if t != MessageTypeText {
return errors.New("not correct type")
}
stripPadding(buf)

bufUnmarshal("duration", buf, &m.Duration)
bufUnmarshal("blob-id", buf, &m.BlobID)
bufUnmarshal("size", buf, &m.Size)
bufUnmarshal("key", buf, &m.Key)

m.ServerID = m.BlobID[0]

return nil
}

func init() {
messageUnmarshal[MessageTypeAudio] = func(mh *MessageHeader, data []byte) (Message, error) {
m := &AudioMessage{
MessageHeader: mh,
}
if err := m.UnmarshalBinary(data); err != nil {
return nil, err
}
return m, nil
}
}
87 changes: 87 additions & 0 deletions message_image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package o3

import (
"bytes"
"errors"
"fmt"
"io/ioutil"
)

// MsgType mock enum
const MessageTypeImage MsgType = 0x2

//ImageMessage represents a image message as sent e2e encrypted to other threema users
type ImageMessage struct {
*MessageHeader
BlobID [16]byte
ServerID byte
Size uint32
Nonce nonce
}

// String returns the message text as string
func (m ImageMessage) String() string {
return fmt.Sprintf("ImageMSG: https://%2x.blob.threema.ch/%16x, Size: %d, Nonce: %24x", m.ServerID, m.BlobID, m.Size, m.Nonce.nonce)
}

// GetImageData return the decrypted Image needs the recipients secret key
func (m ImageMessage) GetData(threemaID *ThreemaID) ([]byte, error) {
return downloadAndDecryptAsym(threemaID, m.BlobID, m.Sender.String(), m.Nonce)
}

// SetDataByFile encrypts and uploads the image by file. Sets the blob info in the ImageMessage. Needs the recipients public key.
func (m *ImageMessage) SetDataByFile(threemaID *ThreemaID, filename string) error {
data, err := ioutil.ReadFile(filename)
if err != nil {
return errors.New("could not load image")
}
return m.SetData(threemaID, data)
}

// SetData encrypts and uploads the image. Sets the blob info in the ImageMessage. Needs the recipients public key.
func (m *ImageMessage) SetData(threemaID *ThreemaID, data []byte) (err error) {
m.Nonce, m.ServerID, m.Size, m.BlobID, err = encryptAsymAndUpload(threemaID, data, m.Recipient.String())
return
}

//Serialize returns a fully serialized byte slice of a TextMessage
func (m ImageMessage) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
bufMarshal("msg-type", buf, MessageTypeImage)
bufMarshal("blob-id", buf, m.BlobID)
bufMarshal("size", buf, m.Size)
bufMarshal("nonce", buf, m.Nonce.nonce)
bufMarshalPadding(buf)

return buf.Bytes(), nil
}

func (m *ImageMessage) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
var t MsgType
bufUnmarshal("read message type", buf, &t)
if t != MessageTypeText {
return errors.New("not correct type")
}
stripPadding(buf)

bufUnmarshal("blob-id", buf, &m.BlobID)
bufUnmarshal("size", buf, &m.Size)
bufUnmarshal("nonce", buf, &m.Nonce.nonce)

m.ServerID = m.BlobID[0]

return nil
}

func init() {
messageUnmarshal[MessageTypeImage] = func(mh *MessageHeader, data []byte) (Message, error) {
m := &ImageMessage{
MessageHeader: mh,
}
if err := m.UnmarshalBinary(data); err != nil {
return nil, err
}
return m, nil
}
}
46 changes: 36 additions & 10 deletions message_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import (
)

// MsgType mock enum
const MessageTypeText MsgType = 0x1
const (
MessageTypeText MsgType = 0x1
MessageTypeGroupText MsgType = 0x41
)

//TextMessage represents a text message as sent e2e encrypted to other threema users
type TextMessage struct {
*MessageHeader
*GroupMessageHeader
Body string
}

Expand All @@ -28,36 +32,58 @@ func (tm TextMessage) String() string {
}

//Serialize returns a fully serialized byte slice of a TextMessage
func (tm TextMessage) MarshalBinary() ([]byte, error) {
func (m TextMessage) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
bufMarshal("msg-type", buf, MessageTypeText)
bufMarshal("body", buf, []byte(tm.Body))
if m.GroupMessageHeader == nil {
bufMarshal("msg-type", buf, MessageTypeText)
} else {
bufMarshal("msg-type", buf, uint8(MessageTypeGroupText))
data, err := m.GroupMessageHeader.MarshalBinary()
bufMarshal("msg-group-header", buf, data)
if err != nil {
return nil, err
}
}
bufMarshal("body", buf, []byte(m.Body))
bufMarshalPadding(buf)

return buf.Bytes(), nil
}

func (tm *TextMessage) UnmarshalBinary(data []byte) error {
func (m *TextMessage) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
var t MsgType
bufUnmarshal("read message type", buf, &t)
if t != MessageTypeText {
if t == MessageTypeGroupText {
m.GroupMessageHeader.UnmarshalBinary(data[1 : GroupMessageHeaderLenght+1])
} else if t != MessageTypeText {
return errors.New("not correct type")
}
stripPadding(buf)

tm.Body = string(buf.Bytes())
m.Body = string(buf.Bytes())
return nil
}

func init() {
messageUnmarshal[MessageTypeText] = func(mh *MessageHeader, data []byte) (Message, error) {
tm := &TextMessage{
m := &TextMessage{
MessageHeader: mh,
}
if err := tm.UnmarshalBinary(data); err != nil {
if err := m.UnmarshalBinary(data); err != nil {
return nil, err
}
return m, nil
}
messageUnmarshal[MessageTypeGroupText] = func(mh *MessageHeader, data []byte) (Message, error) {
m := &TextMessage{
MessageHeader: mh,
GroupMessageHeader: &GroupMessageHeader{},
}

if err := m.UnmarshalBinary(data); err != nil {
return nil, err
}
return tm, nil
return m, nil
}
}
27 changes: 27 additions & 0 deletions messagegroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package o3

import (
"bytes"
)

type GroupMessageHeader struct {
CreatorID IDString
GroupID [8]byte
}

const GroupMessageHeaderLenght = 16

func (msg GroupMessageHeader) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
bufMarshal("gh-creator id", buf, msg.CreatorID)
bufMarshal("gh-group id", buf, msg.GroupID)
return buf.Bytes(), nil
}

func (msg *GroupMessageHeader) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
bufUnmarshal("read group creator", buf, &msg.CreatorID)
bufUnmarshal("read group id", buf, &msg.GroupID)

return nil
}
Loading

0 comments on commit 3b942f8

Please sign in to comment.