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) } } }