seekia/internal/messaging/createMessages/createMessages.go

237 lines
10 KiB
Go

// createMessages provides functions to create Seekia chat messages
// The chat message structure will eventually be documented in /documentation/Specification.md
package createMessages
//TODO: Add an upper limit on bytes, and an upper limit for size of communication.
//TODO: Verify all message values and function inputs.
import "seekia/internal/allowedText"
import "seekia/internal/appValues"
import "seekia/internal/cryptography/blake3"
import "seekia/internal/cryptography/chaPolyShrink"
import "seekia/internal/cryptography/edwardsKeys"
import "seekia/internal/cryptography/kyber"
import "seekia/internal/cryptography/nacl"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import messagepack "github.com/vmihailenco/msgpack/v5"
import "slices"
import "errors"
//Outputs:
// -[]byte: Message bytes
// -[26]byte: Message Hash
// -error
func CreateChatMessage(
networkType byte,
senderIdentityHash [16]byte,
senderIdentityPublicKey [32]byte,
senderIdentityPrivateKey [64]byte,
messageCreationTime int64,
senderCurrentSecretInboxSeed [22]byte,
senderNextSecretInboxSeed [22]byte,
senderDeviceIdentifier [11]byte,
senderLatestChatKeysUpdateTime int64,
communication string,
recipientIdentityHash [16]byte,
recipientInbox [10]byte,
recipientNaclKey [32]byte,
recipientKyberKey [1568]byte,
doubleSealedKeysSealerKey [32]byte)([]byte, [26]byte, error){
// We create the basaldataEncryptionKey, messageCipherKey, and messageCipherKeyHash
basaldataEncryptionKey, err := helpers.GetNewRandom32ByteArray()
if (err != nil) { return nil, [26]byte{}, err }
messageCipherKey, err := blake3.Get32ByteBlake3Hash(basaldataEncryptionKey[:])
if (err != nil) { return nil, [26]byte{}, err }
cipherKeyHash, err := blake3.GetBlake3HashAsBytes(25, messageCipherKey[:])
if (err != nil) { return nil, [26]byte{}, err }
// We create the noncipheredSection, which contains the message inbox, message version, network type, cipherKeyHash, and the doubleSealedKeys
keyPieceA, err := helpers.GetNewRandom32ByteArray()
if (err != nil) { return nil, [26]byte{}, err }
keyPieceB := helpers.XORTwo32ByteArrays(keyPieceA, basaldataEncryptionKey)
naclEncryptedKeyPieceA, err := nacl.EncryptKeyWithNacl(recipientNaclKey, keyPieceA)
if (err != nil) { return nil, [26]byte{}, err }
kyberEncryptedKeyPieceB, err := kyber.EncryptKeyWithKyber(recipientKyberKey, keyPieceB)
if (err != nil) { return nil, [26]byte{}, err }
innerSealedKeyPiecesList := slices.Concat(naclEncryptedKeyPieceA[:], kyberEncryptedKeyPieceB[:])
doubleSealedKeysNonce, err := helpers.GetNewRandom24ByteArray()
if (err != nil) { return nil, [26]byte{}, err }
doubleSealedKeys, err := chaPolyShrink.EncryptChaPolyShrink(innerSealedKeyPiecesList, doubleSealedKeysSealerKey, doubleSealedKeysNonce, false, 0, false, [32]byte{})
if (err != nil) { return nil, [26]byte{}, err }
// We create the DoubleSealedKeys to store the encryption keys for the cipheredMessage and the basaldata
messageVersion := appValues.GetMessageVersion()
cipheredMessageNonce, err := helpers.GetNewRandom24ByteArray()
if (err != nil) { return nil, [26]byte{}, err }
messageVersionEncoded, err := encoding.EncodeMessagePackBytes(messageVersion)
if (err != nil) { return nil, [26]byte{}, err }
networkTypeEncoded, err := encoding.EncodeMessagePackBytes(networkType)
if (err != nil) { return nil, [26]byte{}, err }
recipientInboxEncoded, err := encoding.EncodeMessagePackBytes(recipientInbox)
if (err != nil) { return nil, [26]byte{}, err }
doubleSealedKeysNonceEncoded, err := encoding.EncodeMessagePackBytes(doubleSealedKeysNonce)
if (err != nil) { return nil, [26]byte{}, err }
doubleSealedKeysEncoded, err := encoding.EncodeMessagePackBytes(doubleSealedKeys)
if (err != nil) { return nil, [26]byte{}, err }
cipherKeyHashEncoded, err := encoding.EncodeMessagePackBytes(cipherKeyHash)
if (err != nil) { return nil, [26]byte{}, err }
cipheredMessageNonceEncoded, err := encoding.EncodeMessagePackBytes(cipheredMessageNonce)
if (err != nil) { return nil, [26]byte{}, err }
noncipheredSectionSlice := []messagepack.RawMessage{messageVersionEncoded, networkTypeEncoded, recipientInboxEncoded, doubleSealedKeysNonceEncoded, doubleSealedKeysEncoded, cipherKeyHashEncoded, cipheredMessageNonceEncoded}
noncipheredSectionEncoded, err := encoding.EncodeMessagePackBytes(noncipheredSectionSlice)
if (err != nil) { return nil, [26]byte{}, err }
// The purpose of the noncipheredSectionHash is to prevent someone from tampering with the message and replaying the ciphered portion
// By using the noncipheredSectionHash as the additionalData, the noncipheredSection cannot be tampered with
noncipheredSectionHash, err := blake3.Get32ByteBlake3Hash(noncipheredSectionEncoded)
if (err != nil) { return nil, [26]byte{}, err }
// We create the inner message
timeIsValid := helpers.VerifyCreationTime(messageCreationTime)
if (timeIsValid == false){
return nil, [26]byte{}, errors.New("CreateChatMessage called with invalid messageCreationTime.")
}
timeIsValid = helpers.VerifyCreationTime(senderLatestChatKeysUpdateTime)
if (timeIsValid == false){
return nil, [26]byte{}, errors.New("CreateChatMessage called with invalid senderLatestChatKeysUpdateTime")
}
recipientIdentityType, err := identity.GetIdentityTypeFromIdentityHash(recipientIdentityHash)
if (err != nil){
recipientIdentityHashHex := encoding.EncodeBytesToHexString(recipientIdentityHash[:])
return nil, [26]byte{}, errors.New("CreateChatMessage called with invalid recipientIdentityHash: " + recipientIdentityHashHex)
}
senderIdentityHashExpected, err := identity.ConvertIdentityKeyToIdentityHash(senderIdentityPublicKey, recipientIdentityType)
if (err != nil) {
senderIdentityPublicKeyHex := encoding.EncodeBytesToHexString(senderIdentityPublicKey[:])
return nil, [26]byte{}, errors.New("CreateChatMessage called with invalid senderIdentityPublicKey: " + senderIdentityPublicKeyHex)
}
if (senderIdentityHash != senderIdentityHashExpected){
senderIdentityHashHex := encoding.EncodeBytesToHexString(senderIdentityHash[:])
return nil, [26]byte{}, errors.New("CreateChatMessage called with invalid senderIdentityHash: " + senderIdentityHashHex)
}
recipientIdentityHashEncoded, err := encoding.EncodeMessagePackBytes(recipientIdentityHash)
if (err != nil) { return nil, [26]byte{}, err }
creationTimeEncoded, err := encoding.EncodeMessagePackBytes(messageCreationTime)
if (err != nil) { return nil, [26]byte{}, err }
senderCurrentSecretInboxSeedEncoded, err := encoding.EncodeMessagePackBytes(senderCurrentSecretInboxSeed)
if (err != nil) { return nil, [26]byte{}, err }
senderNextSecretInboxSeedEncoded, err := encoding.EncodeMessagePackBytes(senderNextSecretInboxSeed)
if (err != nil) { return nil, [26]byte{}, err }
senderDeviceIdentifierEncoded, err := encoding.EncodeMessagePackBytes(senderDeviceIdentifier)
if (err != nil) { return nil, [26]byte{}, err }
senderLatestChatKeysUpdateTimeEncoded, err := encoding.EncodeMessagePackBytes(senderLatestChatKeysUpdateTime)
if (err != nil) { return nil, [26]byte{}, err }
messageBasaldataSlice := []messagepack.RawMessage{recipientIdentityHashEncoded, creationTimeEncoded, senderCurrentSecretInboxSeedEncoded, senderNextSecretInboxSeedEncoded, senderDeviceIdentifierEncoded, senderLatestChatKeysUpdateTimeEncoded}
messageBasaldataEncoded, err := encoding.EncodeMessagePackBytes(messageBasaldataSlice)
if (err != nil) { return nil, [26]byte{}, err }
basaldataEncryptionNonce, err := helpers.GetNewRandom24ByteArray()
if (err != nil) { return nil, [26]byte{}, err }
encryptedBasaldata, err := chaPolyShrink.EncryptChaPolyShrink(messageBasaldataEncoded, basaldataEncryptionKey, basaldataEncryptionNonce, false, 0, false, [32]byte{})
if (err != nil) { return nil, [26]byte{}, err }
communicationIsAllowed := allowedText.VerifyStringIsAllowed(communication)
if (communicationIsAllowed == false){
return nil, [26]byte{}, errors.New("CreateChatMessage called with communication containing unallowed characters.")
}
//TODO: Add length restriction to communication
senderIdentityKeyEncoded, err := encoding.EncodeMessagePackBytes(senderIdentityPublicKey)
if (err != nil) { return nil, [26]byte{}, err }
senderIdentityTypeEncoded, err := encoding.EncodeMessagePackBytes(recipientIdentityType)
if (err != nil) { return nil, [26]byte{}, err }
basaldataEncryptionNonceEncoded, err := encoding.EncodeMessagePackBytes(basaldataEncryptionNonce)
if (err != nil) { return nil, [26]byte{}, err }
encryptedBasaldataEncoded, err := encoding.EncodeMessagePackBytes(encryptedBasaldata)
if (err != nil) { return nil, [26]byte{}, err }
communicationEncoded, err := encoding.EncodeMessagePackBytes(communication)
if (err != nil) { return nil, [26]byte{}, err }
innerMessageContentSlice := []messagepack.RawMessage{senderIdentityKeyEncoded, senderIdentityTypeEncoded, basaldataEncryptionNonceEncoded, encryptedBasaldataEncoded, communicationEncoded}
innerMessageContentEncoded, err := encoding.EncodeMessagePackBytes(innerMessageContentSlice)
if (err != nil) { return nil, [26]byte{}, err }
innerMessageContentHash, err := blake3.Get32ByteBlake3Hash(innerMessageContentEncoded)
if (err != nil) { return nil, [26]byte{}, err }
innerMessageSignature := edwardsKeys.CreateSignature(senderIdentityPrivateKey, innerMessageContentHash)
signatureEncoded, err := encoding.EncodeMessagePackBytes(innerMessageSignature)
if (err != nil) { return nil, [26]byte{}, err }
innerMessageSlice := []messagepack.RawMessage{signatureEncoded, innerMessageContentEncoded}
innerMessageBytes, err := encoding.EncodeMessagePackBytes(innerMessageSlice)
if (err != nil) { return nil, [26]byte{}, err }
cipheredMessage, err := chaPolyShrink.EncryptChaPolyShrink(innerMessageBytes, messageCipherKey, cipheredMessageNonce, true, 1000, true, noncipheredSectionHash)
if (err != nil) { return nil, [26]byte{}, err }
cipheredMessageEncoded, err := encoding.EncodeMessagePackBytes(cipheredMessage)
if (err != nil) { return nil, [26]byte{}, err }
// We encode the outer message
finalMessageSlice := []messagepack.RawMessage{noncipheredSectionEncoded, cipheredMessageEncoded}
finalMessageBytes, err := encoding.EncodeMessagePackBytes(finalMessageSlice)
if (err != nil) { return nil, [26]byte{}, err }
messageHash, err := blake3.GetBlake3HashAsBytes(26, finalMessageBytes)
if (err != nil) { return nil, [26]byte{}, err }
messageHashArray := [26]byte(messageHash)
return finalMessageBytes, messageHashArray, nil
}