613 lines
26 KiB
Go
613 lines
26 KiB
Go
|
|
// readMessages provides functions to read Seekia chat messages
|
|
// The chat message structure will eventually be documented in /documentation/Specification.md
|
|
|
|
package readMessages
|
|
|
|
//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/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 "bytes"
|
|
import "errors"
|
|
|
|
func VerifyMessageHashHex(inputHash string)bool{
|
|
|
|
isValid := helpers.VerifyHexString(26, inputHash)
|
|
|
|
return isValid
|
|
}
|
|
|
|
func VerifyMessageCipherKeyHex(inputCipherKey string)bool{
|
|
|
|
isValid := helpers.VerifyHexString(32, inputCipherKey)
|
|
|
|
return isValid
|
|
}
|
|
|
|
func ReadMessageHashHex(inputMessageHash string)([26]byte, error){
|
|
|
|
messageHashBytes, err := encoding.DecodeHexStringToBytes(inputMessageHash)
|
|
if (err != nil){
|
|
return [26]byte{}, errors.New("ReadMessageHashHex called with invalid MessageHash: Not Hex: " + inputMessageHash)
|
|
}
|
|
|
|
if (len(messageHashBytes) != 26){
|
|
return [26]byte{}, errors.New("ReadMessageHashHex called with invalid MessageHash: Invalid length: " + inputMessageHash)
|
|
}
|
|
|
|
messageHashArray := [26]byte(messageHashBytes)
|
|
|
|
return messageHashArray, nil
|
|
}
|
|
|
|
func ReadMessageCipherKeyHex(inputCipherKey string)([32]byte, error){
|
|
|
|
cipherKeyBytes, err := encoding.DecodeHexStringToBytes(inputCipherKey)
|
|
if (err != nil){
|
|
return [32]byte{}, errors.New("ReadMessageCipherKeyHex called with invalid inputCipherKey: Not Hex: " + inputCipherKey)
|
|
}
|
|
|
|
if (len(cipherKeyBytes) != 32){
|
|
return [32]byte{}, errors.New("ReadMessageCipherKeyHex called with invalid inputCipherKey: Invalid length: " + inputCipherKey)
|
|
}
|
|
|
|
cipherKeyArray := [32]byte(cipherKeyBytes)
|
|
|
|
return cipherKeyArray, nil
|
|
}
|
|
|
|
func VerifyMessageCipherKeyHashHex(inputCipherKeyHash string)bool{
|
|
|
|
isValid := helpers.VerifyHexString(25, inputCipherKeyHash)
|
|
|
|
return isValid
|
|
}
|
|
|
|
func ConvertMessageCipherKeyToCipherKeyHash(inputMessageCipherKey [32]byte)([25]byte, error){
|
|
|
|
cipherKeyHash, err := blake3.GetBlake3HashAsBytes(25, inputMessageCipherKey[:])
|
|
if (err != nil) { return [25]byte{}, err }
|
|
|
|
cipherKeyHashArray := [25]byte(cipherKeyHash)
|
|
|
|
return cipherKeyHashArray, nil
|
|
}
|
|
|
|
|
|
// This function reads a chat message's public data
|
|
// It also computes and returns the message hash
|
|
// It does not decrypt the message.
|
|
// Outputs:
|
|
// -bool: Able to read
|
|
// -[26]byte: Message Hash
|
|
// -int: Message version
|
|
// -byte: Network type (1 == Mainnet, 2 == Testnet)
|
|
// -[10]byte: Message Inbox
|
|
// -messagepack.RawMessage: Message noncipheredSection bytes (needed to decrypt the cipheredMessage)
|
|
// -[24]byte: DoubleSealedKeysNonce
|
|
// -[]byte: DoubleSealedKeys
|
|
// -[25]byte: CipherKeyHash
|
|
// -[24]byte: CipheredMessageNonce
|
|
// -[]byte: CipheredMessage
|
|
// -error (will return err if there is a bug)
|
|
func ReadChatMessagePublicDataAndHash(verifyData bool, inputMessage []byte)(bool, [26]byte, int, byte, [10]byte, messagepack.RawMessage, [24]byte, []byte, [25]byte, [24]byte, []byte, error){
|
|
|
|
ableToRead, messageVersion, messageNetworkType, messageInbox, messageNoncipheredSectionBytes, messageDoubleSealedKeysNonce, messageDoubleSealedKeys, messageCipherKeyHash, messageCipheredMessageNonce, messageCipheredMessage, err := ReadChatMessagePublicData(verifyData, inputMessage)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, err }
|
|
if (ableToRead == false){
|
|
return false, [26]byte{}, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
messageHashSlice, err := blake3.GetBlake3HashAsBytes(26, inputMessage)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, err }
|
|
|
|
messageHash := [26]byte(messageHashSlice)
|
|
|
|
return true, messageHash, messageVersion, messageNetworkType, messageInbox, messageNoncipheredSectionBytes, messageDoubleSealedKeysNonce, messageDoubleSealedKeys, messageCipherKeyHash, messageCipheredMessageNonce, messageCipheredMessage, nil
|
|
}
|
|
|
|
// This function reads a chat message's public data, and optionally verifies the message's public data.
|
|
// It does not compute the message hash, thus it is faster
|
|
// It does not decrypt the message.
|
|
// Outputs:
|
|
// -bool: Able to read
|
|
// -int: Message version
|
|
// -byte: Network type (1 == Mainnet, 2 == Testnet)
|
|
// -[10]byte: Message Inbox
|
|
// -messagepack.RawMessage: Message noncipheredSection bytes (needed to decrypt the cipheredMessage)
|
|
// -[24]byte: DoubleSealedKeysNonce
|
|
// -[]byte: DoubleSealedKeys
|
|
// -[25]byte: CipherKeyHash
|
|
// -[24]byte: CipheredMessageNonce
|
|
// -[]byte: CipheredMessage
|
|
// -error (will return err if there is a bug)
|
|
func ReadChatMessagePublicData(verifyData bool, inputMessage []byte)(bool, int, byte, [10]byte, messagepack.RawMessage, [24]byte, []byte, [25]byte, [24]byte, []byte, error){
|
|
|
|
var messageSlice []messagepack.RawMessage
|
|
|
|
err := encoding.DecodeMessagePackBytes(false, inputMessage, &messageSlice)
|
|
if (err != nil){
|
|
// Cannot read message: Invalid messagepack
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
if (len(messageSlice) != 2){
|
|
// Cannot read message: Invalid messagepack
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
messageNoncipheredSectionEncoded := messageSlice[0]
|
|
cipheredMessageEncoded := messageSlice[1]
|
|
|
|
var noncipheredSectionSlice []messagepack.RawMessage
|
|
|
|
err = encoding.DecodeMessagePackBytes(false, messageNoncipheredSectionEncoded, &noncipheredSectionSlice)
|
|
if (err != nil){
|
|
// Malformed message: Invalid message content
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
if (len(noncipheredSectionSlice) != 7){
|
|
// Malformed message: Invalid message noncipheredSection
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
messageVersionEncoded := noncipheredSectionSlice[0]
|
|
networkTypeEncoded := noncipheredSectionSlice[1]
|
|
recipientInboxEncoded := noncipheredSectionSlice[2]
|
|
doubleSealedKeysNonceEncoded := noncipheredSectionSlice[3]
|
|
doubleSealedKeysEncoded := noncipheredSectionSlice[4]
|
|
cipherKeyHashEncoded := noncipheredSectionSlice[5]
|
|
cipheredMessageNonceEncoded := noncipheredSectionSlice[6]
|
|
|
|
messageVersion, err := encoding.DecodeRawMessagePackToInt(messageVersionEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid message version
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
if (messageVersion != 1){
|
|
// We cannot read the message. It is created for a newer version of Seekia
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
messageNetworkType, err := encoding.DecodeRawMessagePackToByte(networkTypeEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid networkType
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
isValid := helpers.VerifyNetworkType(messageNetworkType)
|
|
if (isValid == false){
|
|
// Malformed message: Invalid networkType
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
recipientInbox, err := encoding.DecodeRawMessagePackTo10ByteArray(recipientInboxEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid recipient inbox
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
doubleSealedKeysNonce, err := encoding.DecodeRawMessagePackTo24ByteArray(doubleSealedKeysNonceEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid doubleSealedKeysNonce
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
doubleSealedKeys, err := encoding.DecodeRawMessagePackToBytes(doubleSealedKeysEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid doubleSealedKeys
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
cipherKeyHash, err := encoding.DecodeRawMessagePackTo25ByteArray(cipherKeyHashEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid cipherKeyHash
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
cipheredMessageNonce, err := encoding.DecodeRawMessagePackTo24ByteArray(cipheredMessageNonceEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid cipheredMessageNonce
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
cipheredMessage, err := encoding.DecodeRawMessagePackToBytes(cipheredMessageEncoded)
|
|
if (err != nil){
|
|
// Malformed message: Invalid cipheredMessage
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
if (verifyData == true){
|
|
|
|
if (len(doubleSealedKeys) != 1684){
|
|
// Invalid doubleSealedKeys: Invalid length
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
|
|
if (len(cipheredMessage) < 3){
|
|
// Invalid Message: Invalid cipheredMessage
|
|
return false, 0, 0, [10]byte{}, nil, [24]byte{}, nil, [25]byte{}, [24]byte{}, nil, nil
|
|
}
|
|
}
|
|
|
|
return true, messageVersion, messageNetworkType, recipientInbox, messageNoncipheredSectionEncoded, doubleSealedKeysNonce, doubleSealedKeys, cipherKeyHash, cipheredMessageNonce, cipheredMessage, nil
|
|
}
|
|
|
|
// This struct defines a chat key set which is used to decrypt a chat message
|
|
// Each user may have multiple key sets, if they have shared newer keys more recently.
|
|
// They keep their old keys so they can decrypt messages sent to their older keys.
|
|
type ChatKeySet struct{
|
|
|
|
NaclPublicKey [32]byte
|
|
NaclPrivateKey [32]byte
|
|
KyberPrivateKey [1536]byte
|
|
}
|
|
|
|
// This function will decrypt a chat message with a recipient device chat keys list
|
|
// This is used by users who are decrypting messages sent to them
|
|
//Inputs:
|
|
// -[]byte: InputMessage
|
|
// -[32]byte: Double Sealed Keys decryption key
|
|
// -[]ChatKeySet: RecipientChatDecryptionKeysList (contains decryption keys for each recipient chat key set)
|
|
//Outputs:
|
|
// -bool: Able to read message (could be false if message is malformed or unable to decrypt)
|
|
// -[26]byte: Message Hash
|
|
// -int: Message Version
|
|
// -byte: Message network type (1 == Mainnet, 2 == Testnet1)
|
|
// -[32]byte: Message Cipher Key
|
|
// -[16]byte: Sender Identity Hash
|
|
// -[16]byte: Recipient Identity Hash
|
|
// -int64: Message creation time (unix)
|
|
// -string: Communication
|
|
// -[22]byte: Sender Current secret inbox seed
|
|
// -[22]byte: Sender Next secret inbox seed
|
|
// -[11]byte: Sender Device Identifier
|
|
// -int64: Sender Latest chat keys update time
|
|
// -error (will return err if non-message inputs are malformed, or there is a bug)
|
|
func ReadChatMessage(inputMessage []byte, doubleSealedKeysDecryptionKey [32]byte, recipientDecryptionKeySetsList []ChatKeySet)(bool, [26]byte, int, byte, [32]byte, [16]byte, [16]byte, int64, string, [22]byte, [22]byte, [11]byte, int64, error){
|
|
|
|
ableToRead, messageHash, messageVersion, messageNetworkType, _, messageNoncipheredSectionBytes, doubleSealedKeysNonce, doubleSealedKeys, expectedCipherKeyHash, cipheredMessageNonce, cipheredMessage, err := ReadChatMessagePublicDataAndHash(true, inputMessage)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
if (ableToRead == false){
|
|
// Invalid message format
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
ableToDecrypt, decryptedBytes, err := chaPolyShrink.DecryptChaPolyShrink(doubleSealedKeys, doubleSealedKeysDecryptionKey, doubleSealedKeysNonce, false, [32]byte{})
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
if (ableToDecrypt == false){
|
|
// doubleSealedKeys cannot be decrypted
|
|
// Sender must be malicious
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
if (len(decryptedBytes) != 1648){
|
|
// Sender must be malicious.
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
naclEncryptedKeyPieceA := [80]byte(decryptedBytes[:80])
|
|
kyberEncryptedKeyPieceB := [1568]byte(decryptedBytes[80:])
|
|
|
|
//Outputs:
|
|
// -bool: Able to decrypt
|
|
// -[32]byte: Basaldata decryption key
|
|
// -error (will be error if inputs are invalid)
|
|
getBasaldataDecryptionKey := func()(bool, [32]byte, error){
|
|
|
|
// This cycles through all chat decryption keys the user has
|
|
// They share new keys on their profile, and delete old keys after a certain time period
|
|
|
|
for _, keySetObject := range recipientDecryptionKeySetsList{
|
|
|
|
recipientNaclPublicKey := keySetObject.NaclPublicKey
|
|
recipientNaclPrivateKey := keySetObject.NaclPrivateKey
|
|
recipientKyberPrivateKey := keySetObject.KyberPrivateKey
|
|
|
|
ableToDecrypt, keyPieceA, err := nacl.DecryptNaclEncryptedKey(naclEncryptedKeyPieceA, recipientNaclPublicKey, recipientNaclPrivateKey)
|
|
if (err != nil) { return false, [32]byte{}, err }
|
|
if (ableToDecrypt == false){
|
|
// Keys are not the correct decryption keys
|
|
// Skip to next keys
|
|
continue
|
|
}
|
|
|
|
keyPieceB, err := kyber.DecryptKyberEncryptedKey(kyberEncryptedKeyPieceB, recipientKyberPrivateKey)
|
|
if (err != nil) { return false, [32]byte{}, err }
|
|
|
|
basaldataDecryptionKey := helpers.XORTwo32ByteArrays(keyPieceA, keyPieceB)
|
|
|
|
return true, basaldataDecryptionKey, nil
|
|
}
|
|
|
|
// We don't have the keys needed to decrypt the message.
|
|
return false, [32]byte{}, nil
|
|
}
|
|
|
|
ableToDecrypt, basaldataDecryptionKey, err := getBasaldataDecryptionKey()
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
if (ableToDecrypt == false){
|
|
// We don't have the keys to decrypt, or message is invalid
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
// CipheredMessageKey is a hash of the Basaldata decryption key
|
|
// This allows users to report messages and reveal only the cipheredMessage
|
|
// This way, only the innerMessage contents are revealed, and the basaldata remains encrypted
|
|
|
|
messageCipherKey, err := blake3.GetBlake3HashAsBytes(32, basaldataDecryptionKey[:])
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
|
|
messageCipherKeyArray := [32]byte(messageCipherKey)
|
|
|
|
messageCipherKeyHash, err := blake3.GetBlake3HashAsBytes(25, messageCipherKey)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
|
|
areEqual := bytes.Equal(messageCipherKeyHash, expectedCipherKeyHash[:])
|
|
if (areEqual == false){
|
|
// Cipher key hash does not match expected cipher key hash
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
ableToRead, senderIdentityHash, senderIdentityType, encryptedBasaldata, basaldataNonce, messageCommunication, err := readCipheredMessage(1, cipheredMessage, messageCipherKeyArray, cipheredMessageNonce, messageNoncipheredSectionBytes)
|
|
if (ableToRead == false){
|
|
// Invalid ciphered message
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
// Now we decrypt basaldata
|
|
|
|
ableToDecrypt, decryptedBasaldataBytes, err := chaPolyShrink.DecryptChaPolyShrink(encryptedBasaldata, basaldataDecryptionKey, basaldataNonce, false, [32]byte{})
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, err }
|
|
if (ableToDecrypt == false) {
|
|
// Unable to decrypt. Sender is malicious.
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
var basaldataSlice []messagepack.RawMessage
|
|
|
|
err = encoding.DecodeMessagePackBytes(false, decryptedBasaldataBytes, &basaldataSlice)
|
|
if (err != nil) {
|
|
// Message basaldata messagepack is invalid
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
if (len(basaldataSlice) != 6){
|
|
// Message basaldata is invalid
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
recipientIdentityHashEncoded := basaldataSlice[0]
|
|
messageCreationTimeUnixEncoded := basaldataSlice[1]
|
|
senderCurrentSecretInboxSeedEncoded := basaldataSlice[2]
|
|
senderNextSecretInboxSeedEncoded := basaldataSlice[3]
|
|
senderDeviceIdentifierEncoded := basaldataSlice[4]
|
|
senderLatestChatKeysUpdateTimeEncoded := basaldataSlice[5]
|
|
|
|
recipientIdentityHash, err := encoding.DecodeRawMessagePackTo16ByteArray(recipientIdentityHashEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid RecipientIdentityHash
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
messageCreationTimeUnix, err := encoding.DecodeRawMessagePackToInt64(messageCreationTimeUnixEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid CreationTime
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
senderCurrentSecretInboxSeed, err := encoding.DecodeRawMessagePackTo22ByteArray(senderCurrentSecretInboxSeedEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid SenderCurrentSecretInboxSeed
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
senderNextSecretInboxSeed, err := encoding.DecodeRawMessagePackTo22ByteArray(senderNextSecretInboxSeedEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid SenderNextSecretInboxSeed
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
senderDeviceIdentifier, err := encoding.DecodeRawMessagePackTo11ByteArray(senderDeviceIdentifierEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid SenderDeviceIdentifier
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
senderLatestChatKeysUpdateTime, err := encoding.DecodeRawMessagePackToInt64(senderLatestChatKeysUpdateTimeEncoded)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid SenderLatestChatKeysUpdateTime
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
recipientIdentityType, err := identity.GetIdentityTypeFromIdentityHash(recipientIdentityHash)
|
|
if (err != nil) {
|
|
// Malformed message basaldata: Invalid RecipientIdentityHash
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
if (senderIdentityType != recipientIdentityType){
|
|
// Malformed message basaldata: Recipient and sender identity types do not match.
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
isValid := helpers.VerifyCreationTime(messageCreationTimeUnix)
|
|
if (isValid == false){
|
|
// Malformed message basaldata: Contains invalid messageCreationTime
|
|
return false, [26]byte{}, 0, 0, [32]byte{}, [16]byte{}, [16]byte{}, 0, "", [22]byte{}, [22]byte{}, [11]byte{}, 0, nil
|
|
}
|
|
|
|
return true, messageHash, messageVersion, messageNetworkType, messageCipherKeyArray, senderIdentityHash, recipientIdentityHash, messageCreationTimeUnix, messageCommunication, senderCurrentSecretInboxSeed, senderNextSecretInboxSeed, senderDeviceIdentifier, senderLatestChatKeysUpdateTime, nil
|
|
}
|
|
|
|
// This is used by moderators to decrypt a chat message
|
|
// This does not decrypt the basaldata
|
|
//Outputs:
|
|
// -bool: Able to read message (could be false if message is malformed or unable to decrypt)
|
|
// -[26]byte: Message Hash
|
|
// -int: Message Version
|
|
// -byte: Message network type (1 == Mainnet, 2 == Testnet1)
|
|
// -[16]byte: Sender Identity Hash
|
|
// -string: Communication
|
|
// -error (will return err if non-message inputs are malformed, or there is a bug)
|
|
func ReadChatMessageWithCipherKey(inputMessage []byte, messageCipherKey [32]byte)(bool, [26]byte, int, byte, [16]byte, string, error){
|
|
|
|
ableToRead, messageHash, messageVersion, messageNetworkType, _, messageNoncipheredSectionBytes, _, _, expectedCipherKeyHash, cipheredMessageNonce, cipheredMessage, err := ReadChatMessagePublicDataAndHash(true, inputMessage)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [16]byte{}, "", err }
|
|
if (ableToRead == false){
|
|
// Invalid message format
|
|
return false, [26]byte{}, 0, 0, [16]byte{}, "", nil
|
|
}
|
|
|
|
cipherKeyHash, err := blake3.GetBlake3HashAsBytes(25, messageCipherKey[:])
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [16]byte{}, "", err }
|
|
|
|
areEqual := bytes.Equal(cipherKeyHash, expectedCipherKeyHash[:])
|
|
if (areEqual == false){
|
|
// Invalid message: Cipher key hash does not match
|
|
return false, [26]byte{}, 0, 0, [16]byte{}, "", nil
|
|
}
|
|
|
|
ableToRead, senderIdentityHash, _, _, _, messageCommunication, err := readCipheredMessage(messageVersion, cipheredMessage, messageCipherKey, cipheredMessageNonce, messageNoncipheredSectionBytes)
|
|
if (err != nil) { return false, [26]byte{}, 0, 0, [16]byte{}, "", err }
|
|
if (ableToRead == false){
|
|
return false, [26]byte{}, 0, 0, [16]byte{}, "", nil
|
|
}
|
|
|
|
return true, messageHash, messageVersion, messageNetworkType, senderIdentityHash, messageCommunication, nil
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Able to read (message is decryptable and well formed)
|
|
// -[16]byte: Sender Identity Hash
|
|
// -string: Sender Identity Type
|
|
// -[]byte: Encrypted Basaldata
|
|
// -[24]byte: Basaldata nonce
|
|
// -string: Communication
|
|
// -error (this will return err if there is a bug in the function)
|
|
func readCipheredMessage(messageVersion int, cipheredMessage []byte, cipheredMessageKey [32]byte, cipheredMessageNonce [24]byte, noncipheredSectionBytes messagepack.RawMessage)(bool, [16]byte, string, []byte, [24]byte, string, error){
|
|
|
|
if (messageVersion != 1){
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", errors.New("readCipheredMessage called with invalid messageVersion")
|
|
}
|
|
|
|
noncipheredSectionHash, err := blake3.Get32ByteBlake3Hash(noncipheredSectionBytes)
|
|
if (err != nil) { return false, [16]byte{}, "", nil, [24]byte{}, "", err }
|
|
|
|
ableToDecrypt, decryptedBytes, err := chaPolyShrink.DecryptChaPolyShrink(cipheredMessage, cipheredMessageKey, cipheredMessageNonce, true, noncipheredSectionHash)
|
|
if (err != nil) { return false, [16]byte{}, "", nil, [24]byte{}, "", err }
|
|
if (ableToDecrypt == false) {
|
|
// Unable to decrypt. Sender is malicious.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
// Now we read inner message
|
|
|
|
var innerMessageSlice []messagepack.RawMessage
|
|
|
|
err = encoding.DecodeMessagePackBytes(false, decryptedBytes, &innerMessageSlice)
|
|
if (err != nil) {
|
|
// Inner message messagepack is invalid
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
if (len(innerMessageSlice) != 2){
|
|
// Inner message messagepack is invalid
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
innerMessageSignatureEncoded := innerMessageSlice[0]
|
|
innerMessageRawMessagepack := innerMessageSlice[1]
|
|
|
|
innerMessageSignature, err := encoding.DecodeRawMessagePackTo64ByteArray(innerMessageSignatureEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Invalid innerMessageSignature
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
var innerMessageContentSlice []messagepack.RawMessage
|
|
|
|
err = encoding.DecodeMessagePackBytes(false, innerMessageRawMessagepack, &innerMessageContentSlice)
|
|
if (err != nil) {
|
|
// Message Inner plaintext messagepack is invalid
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
if (len(innerMessageContentSlice) != 5){
|
|
// Malformed inner message: Invalid inner message content
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
senderIdentityKeyEncoded := innerMessageContentSlice[0]
|
|
senderIdentityTypeEncoded := innerMessageContentSlice[1]
|
|
basaldataNonceEncoded := innerMessageContentSlice[2]
|
|
encryptedBasaldataEncoded := innerMessageContentSlice[3]
|
|
communicationEncoded := innerMessageContentSlice[4]
|
|
|
|
senderIdentityKey, err := encoding.DecodeRawMessagePackTo32ByteArray(senderIdentityKeyEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Sender identity key is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
senderIdentityType, err := encoding.DecodeRawMessagePackToString(senderIdentityTypeEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Sender identity type is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
basaldataNonce, err := encoding.DecodeRawMessagePackTo24ByteArray(basaldataNonceEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Basaldata nonce is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
encryptedBasaldata, err := encoding.DecodeRawMessagePackToBytes(encryptedBasaldataEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Encrypted basaldata is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
messageCommunication, err := encoding.DecodeRawMessagePackToString(communicationEncoded)
|
|
if (err != nil){
|
|
// Malformed inner message: Message communication is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
//TODO: Verify communication is valid
|
|
|
|
if (senderIdentityType != "Mate" && senderIdentityType != "Moderator"){
|
|
// Malformed inner message: Invalid senderIdentityType.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
innerContentHash, err := blake3.Get32ByteBlake3Hash(innerMessageRawMessagepack)
|
|
if (err != nil) { return false, [16]byte{}, "", nil, [24]byte{}, "", err }
|
|
|
|
isValid := edwardsKeys.VerifySignature(senderIdentityKey, innerMessageSignature, innerContentHash)
|
|
if (isValid == false) {
|
|
// Malformed inner message: signature is invalid.
|
|
return false, [16]byte{}, "", nil, [24]byte{}, "", nil
|
|
}
|
|
|
|
senderIdentityHash, err := identity.ConvertIdentityKeyToIdentityHash(senderIdentityKey, senderIdentityType)
|
|
if (err != nil) { return false, [16]byte{}, "", nil, [24]byte{}, "", err }
|
|
|
|
return true, senderIdentityHash, senderIdentityType, encryptedBasaldata, basaldataNonce, messageCommunication, nil
|
|
}
|
|
|
|
|
|
|