Browse Source

Update the message/thread structure, and fix the db seeder

pull/1/head
Tovi Jaeschke-Rogers 2 years ago
parent
commit
d67e4e89ba
13 changed files with 393 additions and 83 deletions
  1. +23
    -0
      Backend/Api/Messages/CreateMessage.go
  2. +17
    -9
      Backend/Api/Messages/MessageThread.go
  3. +1
    -1
      Backend/Api/Routes.go
  4. +0
    -10
      Backend/Database/Friends.go
  5. +2
    -0
      Backend/Database/Init.go
  6. +39
    -0
      Backend/Database/MessageThreadUsers.go
  7. +42
    -0
      Backend/Database/MessageThreads.go
  8. +0
    -13
      Backend/Database/Messages.go
  9. +228
    -34
      Backend/Database/Seeder/MessageSeeder.go
  10. +0
    -1
      Backend/Database/Seeder/Seed.go
  11. +0
    -2
      Backend/Models/Friends.go
  12. +20
    -13
      Backend/Models/Messages.go
  13. +21
    -0
      Backend/Util/Strings.go

+ 23
- 0
Backend/Api/Messages/CreateMessage.go View File

@ -0,0 +1,23 @@
package Messages
import (
"encoding/json"
"fmt"
"net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
)
func CreateMessage(w http.ResponseWriter, r *http.Request) {
var (
message Models.Message
err error
)
err = json.NewDecoder(r.Body).Decode(&message)
if err != nil {
return
}
fmt.Println(message)
}

+ 17
- 9
Backend/Api/Messages/MessageThread.go View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" "git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" "git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -11,28 +12,35 @@ import (
func MessageThread(w http.ResponseWriter, r *http.Request) { func MessageThread(w http.ResponseWriter, r *http.Request) {
var ( var (
messages []Models.Message
urlVars map[string]string
threadID string
returnJson []byte
ok bool
err error
userData Models.User
messageThread Models.MessageThread
urlVars map[string]string
threadKey string
returnJson []byte
ok bool
err error
) )
userData, err = Auth.CheckCookieCurrentUser(w, r)
if !ok {
http.Error(w, "Forbidden", http.StatusUnauthorized)
return
}
urlVars = mux.Vars(r) urlVars = mux.Vars(r)
threadID, ok = urlVars["threadID"]
threadKey, ok = urlVars["threadKey"]
if !ok { if !ok {
http.Error(w, "Not Found", http.StatusNotFound) http.Error(w, "Not Found", http.StatusNotFound)
return return
} }
messages, err = Database.GetMessagesByThreadId(threadID)
messageThread, err = Database.GetMessageThreadById(threadKey, userData)
if !ok { if !ok {
http.Error(w, "Not Found", http.StatusNotFound) http.Error(w, "Not Found", http.StatusNotFound)
return return
} }
returnJson, err = json.MarshalIndent(messages, "", " ")
returnJson, err = json.MarshalIndent(messageThread, "", " ")
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError) http.Error(w, "Error", http.StatusInternalServerError)
return return


+ 1
- 1
Backend/Api/Routes.go View File

@ -66,5 +66,5 @@ func InitApiEndpoints(router *mux.Router) {
authApi.HandleFunc("/friend/{userID}/request", Friends.FriendRequest).Methods("POST") authApi.HandleFunc("/friend/{userID}/request", Friends.FriendRequest).Methods("POST")
// Define routes for messages // Define routes for messages
authApi.HandleFunc("/messages/{threadID}", Messages.MessageThread).Methods("GET")
authApi.HandleFunc("/messages/{threadKey}", Messages.MessageThread).Methods("GET")
} }

+ 0
- 10
Backend/Database/Friends.go View File

@ -2,7 +2,6 @@ package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" "git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"github.com/gofrs/uuid"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
@ -36,15 +35,6 @@ func GetFriendsByUserId(userID string) ([]Models.Friend, error) {
} }
func CreateFriendRequest(friend *Models.Friend) error { func CreateFriendRequest(friend *Models.Friend) error {
var (
err error
)
friend.ThreadID, err = uuid.NewV1()
if err != nil {
return err
}
return DB.Session(&gorm.Session{FullSaveAssociations: true}). return DB.Session(&gorm.Session{FullSaveAssociations: true}).
Create(friend). Create(friend).
Error Error


+ 2
- 0
Backend/Database/Init.go View File

@ -22,6 +22,8 @@ func GetModels() []interface{} {
&Models.Friend{}, &Models.Friend{},
&Models.MessageData{}, &Models.MessageData{},
&Models.Message{}, &Models.Message{},
&Models.MessageThread{},
&Models.MessageThreadUser{},
} }
} }


+ 39
- 0
Backend/Database/MessageThreadUsers.go View File

@ -0,0 +1,39 @@
package Database
import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func GetMessageThreadUserById(id string) (Models.MessageThreadUser, error) {
var (
message Models.MessageThreadUser
err error
)
err = DB.Preload(clause.Associations).
First(&message, "id = ?", id).
Error
return message, err
}
func CreateMessageThreadUser(messageThreadUser *Models.MessageThreadUser) error {
var (
err error
)
err = DB.Session(&gorm.Session{FullSaveAssociations: true}).
Create(messageThreadUser).
Error
return err
}
func DeleteMessageThreadUser(messageThreadUser *Models.MessageThreadUser) error {
return DB.Session(&gorm.Session{FullSaveAssociations: true}).
Delete(messageThreadUser).
Error
}

+ 42
- 0
Backend/Database/MessageThreads.go View File

@ -0,0 +1,42 @@
package Database
import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func GetMessageThreadById(id string, user Models.User) (Models.MessageThread, error) {
var (
messageThread Models.MessageThread
err error
)
err = DB.Preload(clause.Associations).
Where("id = ?", id).
Where("user_id = ?", user.ID).
First(&messageThread).
Error
return messageThread, err
}
func CreateMessageThread(messageThread *Models.MessageThread) error {
return DB.Session(&gorm.Session{FullSaveAssociations: true}).
Create(messageThread).
Error
}
func UpdateMessageThread(messageThread *Models.MessageThread) error {
return DB.Session(&gorm.Session{FullSaveAssociations: true}).
Where("id = ?", messageThread.ID).
Updates(messageThread).
Error
}
func DeleteMessageThread(messageThread *Models.MessageThread) error {
return DB.Session(&gorm.Session{FullSaveAssociations: true}).
Delete(messageThread).
Error
}

+ 0
- 13
Backend/Database/Messages.go View File

@ -20,19 +20,6 @@ func GetMessageById(id string) (Models.Message, error) {
return message, err return message, err
} }
func GetMessagesByThreadId(id string) ([]Models.Message, error) {
var (
messages []Models.Message
err error
)
err = DB.Preload(clause.Associations).
Find(&messages, "thread_id = ?", id).
Error
return messages, err
}
func CreateMessage(message *Models.Message) error { func CreateMessage(message *Models.Message) error {
var ( var (
err error err error


+ 228
- 34
Backend/Database/Seeder/MessageSeeder.go View File

@ -8,10 +8,12 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/sha512" "crypto/sha512"
"encoding/pem" "encoding/pem"
"fmt"
"hash" "hash"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" "git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" "git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -39,27 +41,17 @@ func PKCS5Padding(ciphertext []byte, blockSize int, after int) []byte {
return append(ciphertext, padtext...) return append(ciphertext, padtext...)
} }
func seedMessage(primaryUser, secondaryUser Models.User, threadID uuid.UUID, i int) error {
func generateAesKey() (*pem.Block, cipher.BlockMode) {
var ( var (
messageKey Models.Message
messageData Models.MessageData
block cipher.Block
mode cipher.BlockMode
pemBlock *pem.Block pemBlock *pem.Block
block cipher.Block
plaintext string
ciphertext []byte
bKey []byte
bIV []byte
bPlaintext []byte
bKey []byte
bIV []byte
err error err error
) )
plaintext = "Test Message"
bKey = make([]byte, 32) bKey = make([]byte, 32)
_, err = rand.Read(bKey) _, err = rand.Read(bKey)
if err != nil { if err != nil {
@ -70,7 +62,6 @@ func seedMessage(primaryUser, secondaryUser Models.User, threadID uuid.UUID, i i
if err != nil { if err != nil {
panic(err) panic(err)
} }
bPlaintext = PKCS5Padding([]byte(plaintext), aes.BlockSize, len(plaintext))
pemBlock = &pem.Block{ pemBlock = &pem.Block{
Type: "AES KEY", Type: "AES KEY",
@ -82,53 +73,256 @@ func seedMessage(primaryUser, secondaryUser Models.User, threadID uuid.UUID, i i
panic(err) panic(err)
} }
ciphertext = make([]byte, len(bPlaintext))
return pemBlock, cipher.NewCBCEncrypter(block, bIV)
}
func seedMessage(
primaryUser Models.User,
primaryUserThreadKey, secondaryUserThreadKey string,
thread Models.MessageThread,
i int,
) error {
var (
message Models.Message
messageData Models.MessageData
messagePemBlock *pem.Block
messageMode cipher.BlockMode
plaintext string
dataCiphertext []byte
senderIdCiphertext []byte
bPlaintext []byte
bSenderIdPlaintext []byte
err error
)
plaintext = "Test Message"
bPlaintext = PKCS5Padding([]byte(plaintext), aes.BlockSize, len(plaintext))
bSenderIdPlaintext = PKCS5Padding(primaryUser.ID.Bytes(), aes.BlockSize, len(primaryUser.ID.Bytes()))
dataCiphertext = make([]byte, len(bPlaintext))
senderIdCiphertext = make([]byte, len(bSenderIdPlaintext))
mode = cipher.NewCBCEncrypter(block, bIV)
messagePemBlock, messageMode = generateAesKey()
mode.CryptBlocks(ciphertext, bPlaintext)
messageMode.CryptBlocks(dataCiphertext, bPlaintext)
messageMode.CryptBlocks(senderIdCiphertext, bSenderIdPlaintext)
messageData = Models.MessageData{ messageData = Models.MessageData{
Data: ciphertext,
Data: dataCiphertext,
SenderID: senderIdCiphertext,
} }
messageKey = Models.Message{
UserID: primaryUser.ID,
message = Models.Message{
MessageData: messageData, MessageData: messageData,
MessageType: "sender",
RelationalUserId: encryptWithPublicKey(secondaryUser.ID.Bytes(), decodedPublicKey),
SymmetricKey: string(pem.EncodeToMemory(pemBlock)),
SymmetricKey: encryptWithPublicKey(pem.EncodeToMemory(messagePemBlock), decodedPublicKey),
MessageThreadKey: primaryUserThreadKey,
}
err = Database.CreateMessage(&message)
if err != nil {
return err
}
// The symmetric key would be encrypted with secondary users public key in production
// But due to using the same pub/priv key pair for all users, we will just duplicate it
message = Models.Message{
MessageDataID: message.MessageDataID,
SymmetricKey: encryptWithPublicKey(pem.EncodeToMemory(messagePemBlock), decodedPublicKey),
MessageThreadKey: secondaryUserThreadKey,
}
err = Database.CreateMessage(&message)
if err != nil {
return err
} }
return Database.CreateMessage(&messageKey)
return err
}
func seedMessageThread(threadPemBlock *pem.Block, threadMode cipher.BlockMode) (Models.MessageThread, error) {
var (
messageThread Models.MessageThread
name string
bNamePlaintext []byte
nameCiphertext []byte
err error
)
name = "Test Conversation"
bNamePlaintext = PKCS5Padding([]byte(name), aes.BlockSize, len(name))
nameCiphertext = make([]byte, len(bNamePlaintext))
threadMode.CryptBlocks(nameCiphertext, bNamePlaintext)
messageThread = Models.MessageThread{
Name: nameCiphertext,
}
err = Database.CreateMessageThread(&messageThread)
return messageThread, err
}
func seedUpdateMessageThreadUsers(
userJson string,
threadPemBlock *pem.Block,
threadMode cipher.BlockMode,
messageThread Models.MessageThread,
) (Models.MessageThread, error) {
var (
bUsersPlaintext []byte
usersCiphertext []byte
err error
)
bUsersPlaintext = PKCS5Padding([]byte(userJson), aes.BlockSize, len(userJson))
usersCiphertext = make([]byte, len(bUsersPlaintext))
threadMode.CryptBlocks(usersCiphertext, bUsersPlaintext)
messageThread.Users = usersCiphertext
err = Database.UpdateMessageThread(&messageThread)
return messageThread, err
}
func seedMessageThreadUser(
user Models.User,
threadID uuid.UUID,
messageThreadKey string,
threadPemBlock *pem.Block,
threadMode cipher.BlockMode,
) (Models.MessageThreadUser, error) {
var (
messageThreadUser Models.MessageThreadUser
bThreadIdPlaintext []byte
threadIdCiphertext []byte
bKeyPlaintext []byte
keyCiphertext []byte
bAdminPlaintext []byte
adminCiphertext []byte
err error
)
bThreadIdPlaintext = PKCS5Padding(threadID.Bytes(), aes.BlockSize, len(threadID.String()))
threadIdCiphertext = make([]byte, len(bThreadIdPlaintext))
bKeyPlaintext = PKCS5Padding([]byte(messageThreadKey), aes.BlockSize, len(messageThreadKey))
keyCiphertext = make([]byte, len(bKeyPlaintext))
bAdminPlaintext = PKCS5Padding([]byte("true"), aes.BlockSize, len("true"))
adminCiphertext = make([]byte, len(bAdminPlaintext))
threadMode.CryptBlocks(threadIdCiphertext, bThreadIdPlaintext)
threadMode.CryptBlocks(keyCiphertext, bKeyPlaintext)
threadMode.CryptBlocks(adminCiphertext, bAdminPlaintext)
messageThreadUser = Models.MessageThreadUser{
UserID: user.ID,
MessageThreadID: threadIdCiphertext,
MessageThreadKey: keyCiphertext,
Admin: adminCiphertext,
SymmetricKey: encryptWithPublicKey(pem.EncodeToMemory(threadPemBlock), decodedPublicKey),
}
err = Database.CreateMessageThreadUser(&messageThreadUser)
return messageThreadUser, err
} }
func SeedMessages() { func SeedMessages() {
var ( var (
primaryUser Models.User
secondaryUser Models.User
threadID uuid.UUID
i int
err error
messageThread Models.MessageThread
threadPemBlock *pem.Block
threadMode cipher.BlockMode
primaryUser Models.User
primaryUserThreadKey string
secondaryUser Models.User
secondaryUserThreadKey string
userJson string
thread Models.MessageThread
i int
err error
) )
threadPemBlock, threadMode = generateAesKey()
messageThread, err = seedMessageThread(threadPemBlock, threadMode)
primaryUserThreadKey = Util.RandomString(32)
secondaryUserThreadKey = Util.RandomString(32)
primaryUser, err = Database.GetUserByUsername("testUser") primaryUser, err = Database.GetUserByUsername("testUser")
if err != nil { if err != nil {
panic(err) panic(err)
} }
_, err = seedMessageThreadUser(
primaryUser,
messageThread.ID,
primaryUserThreadKey,
threadPemBlock,
threadMode,
)
secondaryUser, err = Database.GetUserByUsername("testUser2") secondaryUser, err = Database.GetUserByUsername("testUser2")
if err != nil { if err != nil {
panic(err) panic(err)
} }
threadID, err = uuid.NewV4()
if err != nil {
panic(err)
_, err = seedMessageThreadUser(
secondaryUser,
messageThread.ID,
secondaryUserThreadKey,
threadPemBlock,
threadMode,
)
userJson = fmt.Sprintf(
`
[
{
"id": "%s",
"username": "%s",
"admin": "true"
},
{
"id": "%s",
"username": "%s",
"admin": "true"
} }
]
`,
primaryUser.ID.String(),
primaryUser.Username,
secondaryUser.ID.String(),
secondaryUser.Username,
)
messageThread, err = seedUpdateMessageThreadUsers(
userJson,
threadPemBlock,
threadMode,
messageThread,
)
for i = 0; i <= 20; i++ { for i = 0; i <= 20; i++ {
err = seedMessage(primaryUser, secondaryUser, threadID, i)
err = seedMessage(
primaryUser,
primaryUserThreadKey,
secondaryUserThreadKey,
thread,
i,
)
if err != nil { if err != nil {
panic(err) panic(err)
} }


+ 0
- 1
Backend/Database/Seeder/Seed.go View File

@ -31,7 +31,6 @@ func Seed() {
err error err error
) )
// TODO: Fix this parsing
block, _ = pem.Decode([]byte(publicKey)) block, _ = pem.Decode([]byte(publicKey))
decKey, err = x509.ParsePKIXPublicKey(block.Bytes) decKey, err = x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {


+ 0
- 2
Backend/Models/Friends.go View File

@ -12,7 +12,5 @@ type Friend struct {
UserID uuid.UUID `gorm:"type:uuid;column:user_id;not null;" json:"user_id"` UserID uuid.UUID `gorm:"type:uuid;column:user_id;not null;" json:"user_id"`
User User `json:"user"` User User `json:"user"`
FriendID []byte `gorm:"not null" json:"friend_id"` // Stored encrypted FriendID []byte `gorm:"not null" json:"friend_id"` // Stored encrypted
ThreadID uuid.UUID `gorm:"type:uuid;column:thread_id;not null;" json:"thread_id"`
Thread []Message `gorm:"foreignKey:thread_id" json:"-"`
AcceptedAt time.Time `json:"accepted_at"` AcceptedAt time.Time `json:"accepted_at"`
} }

+ 20
- 13
Backend/Models/Messages.go View File

@ -2,25 +2,32 @@ package Models
import "github.com/gofrs/uuid" import "github.com/gofrs/uuid"
const (
MessageTypeSender = "sender"
MessageTypeReceiver = "reciever"
)
type MessageData struct { type MessageData struct {
Base Base
Data []byte `gorm:"not null" json:"data"` // Stored encrypted
Data []byte `gorm:"not null" json:"data"` // Stored encrypted
SenderID []byte `gorm:"not null" json:"sender_id"`
} }
// TODO: Rename this to something better
type Message struct { type Message struct {
Base Base
ThreadID uuid.UUID `gorm:"not null" json:"thread_id"`
UserID uuid.UUID `json:"-"`
User User `json:"user"`
MessageDataID uuid.UUID `json:"-"` MessageDataID uuid.UUID `json:"-"`
MessageData MessageData `json:"message_data"` MessageData MessageData `json:"message_data"`
MessageType string `gorm:"not null" json:"message_type"` // sender / reciever
RelationalUserId []byte `gorm:"not null" json:"relational_user_id"` // Stored encrypted. UserID for the user this message is in relation to
SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted
SymmetricKey []byte `gorm:"not null" json:"symmetric_key"` // Stored encrypted
MessageThreadKey string `gorm:"not null" json:"message_thread_key"`
}
type MessageThread struct {
Base
Name []byte `gorm:"not null" json:"name"`
Users []byte `json:"users"`
}
type MessageThreadUser struct {
Base
UserID uuid.UUID `gorm:"type:uuid;column:user_id;not null;" json:"user_id"`
User User `json:"user"`
MessageThreadID []byte `gorm:"not null" json:"message_thread_link_id"`
MessageThreadKey []byte `gorm:"not null" json:"message_thread_key"`
Admin []byte `gorm:"not null" json:"admin"` // Bool if user is admin of thread, stored encrypted
SymmetricKey []byte `gorm:"not null" json:"symmetric_key"` // Stored encrypted
} }

+ 21
- 0
Backend/Util/Strings.go View File

@ -0,0 +1,21 @@
package Util
import (
"math/rand"
)
var (
letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
)
func RandomString(n int) string {
var (
b []rune
i int
)
b = make([]rune, n)
for i = range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}

Loading…
Cancel
Save