package Seeder
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"hash"
|
|
|
|
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
|
|
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
|
|
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
|
|
"github.com/gofrs/uuid"
|
|
)
|
|
|
|
// EncryptWithPublicKey encrypts data with public key
|
|
func encryptWithPublicKey(msg []byte, pub *rsa.PublicKey) []byte {
|
|
var (
|
|
hash hash.Hash
|
|
)
|
|
|
|
hash = sha256.New()
|
|
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ciphertext
|
|
}
|
|
|
|
func PKCS5Padding(ciphertext []byte, blockSize int, after int) []byte {
|
|
var (
|
|
padding int
|
|
padtext []byte
|
|
)
|
|
padding = (blockSize - len(ciphertext)%blockSize)
|
|
padtext = bytes.Repeat([]byte{byte(padding)}, padding)
|
|
return append(ciphertext, padtext...)
|
|
}
|
|
|
|
func generateAesKey() (*pem.Block, cipher.BlockMode) {
|
|
var (
|
|
pemBlock *pem.Block
|
|
block cipher.Block
|
|
|
|
bKey []byte
|
|
bIV []byte
|
|
|
|
err error
|
|
)
|
|
|
|
bKey = make([]byte, 32)
|
|
_, err = rand.Read(bKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
bIV = make([]byte, 16)
|
|
_, err = rand.Read(bIV)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
pemBlock = &pem.Block{
|
|
Type: "AES KEY",
|
|
Bytes: bKey,
|
|
}
|
|
|
|
block, err = aes.NewCipher(bKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
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))
|
|
|
|
messagePemBlock, messageMode = generateAesKey()
|
|
|
|
messageMode.CryptBlocks(dataCiphertext, bPlaintext)
|
|
messageMode.CryptBlocks(senderIdCiphertext, bSenderIdPlaintext)
|
|
|
|
messageData = Models.MessageData{
|
|
Data: dataCiphertext,
|
|
SenderID: senderIdCiphertext,
|
|
}
|
|
|
|
message = Models.Message{
|
|
MessageData: messageData,
|
|
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 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() {
|
|
var (
|
|
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")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
_, err = seedMessageThreadUser(
|
|
primaryUser,
|
|
messageThread.ID,
|
|
primaryUserThreadKey,
|
|
threadPemBlock,
|
|
threadMode,
|
|
)
|
|
|
|
secondaryUser, err = Database.GetUserByUsername("testUser2")
|
|
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++ {
|
|
err = seedMessage(
|
|
primaryUser,
|
|
primaryUserThreadKey,
|
|
secondaryUserThreadKey,
|
|
thread,
|
|
i,
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|