seekia/internal/messaging/peerSecretInboxes/peerSecretInboxes.go

355 lines
14 KiB
Go

// peerSecretInboxes provides functions to manage secret inboxes of users that we are chatting with
package peerSecretInboxes
// Secret inboxes are inboxes that users send to us in messages
// They are used to obscure the identity of the recipient, unlike public inboxes.
// Messages should only be sent to them during their epoch.
// After a peer's secret inbox epoch passes, the inbox is expired, and can be deleted.
// This package keeps track of the peer secret inboxes we should be sending our messages to
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/messaging/inbox"
import "seekia/internal/messaging/peerDevices"
import "seekia/internal/messaging/secretInboxEpoch"
import "seekia/internal/myDatastores/myMapList"
import "errors"
import "sync"
import "time"
// This will be locked whenever we update the map list
var updatingPeerSecretInboxesMapListMutex sync.Mutex
var peerSecretInboxesMapListDatastore *myMapList.MyMapList
// This function must be called whenever an app user signs in
func InitializePeerSecretInboxesDatastore()error{
updatingPeerSecretInboxesMapListMutex.Lock()
defer updatingPeerSecretInboxesMapListMutex.Unlock()
newPeerSecretInboxesMapListDatastore, err := myMapList.CreateNewMapList("PeerSecretInboxes")
if (err != nil) { return err }
peerSecretInboxesMapListDatastore = newPeerSecretInboxesMapListDatastore
return nil
}
//Outputs:
// -bool: Parameters exist
// -error
func AddPeerConversationSecretInboxSeeds(myIdentityHash [16]byte, peerIdentityHash [16]byte, networkType byte, messageSentTime int64, peerCurrentSecretInboxSeed [22]byte, peerNextSecretInboxSeed [22]byte)(bool, error){
myIdentityHashString, myIdentityType, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
if (err != nil){
myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
return false, errors.New("AddPeerConversationSecretInboxSeeds called with invalid myIdentityHash: " + myIdentityHashHex)
}
peerIdentityHashString, peerIdentityType, err := identity.EncodeIdentityHashBytesToString(peerIdentityHash)
if (err != nil) {
peerIdentityHashHex := encoding.EncodeBytesToHexString(peerIdentityHash[:])
return false, errors.New("AddPeerConversationSecretInboxSeeds called with invalid peerIdentityHash: " + peerIdentityHashHex)
}
if (myIdentityType != peerIdentityType){
return false, errors.New("AddPeerConversationSecretInboxSeeds called with mismatched my/peer identityTypes.")
}
networkTypeIsValid := helpers.VerifyNetworkType(networkType)
if (networkTypeIsValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, errors.New("AddPeerConversationSecretInboxSeeds called with invalid networkType: " + networkTypeString)
}
parametersExist, currentSecretInboxStartTime, currentSecretInboxEndTime, nextSecretInboxStartTime, nextSecretInboxEndTime, err := secretInboxEpoch.GetSecretInboxEpochStartAndEndTimes(networkType, messageSentTime)
if (err != nil) { return false, err }
if (parametersExist == false){
return false, nil
}
updatingPeerSecretInboxesMapListMutex.Lock()
defer updatingPeerSecretInboxesMapListMutex.Unlock()
peerSecretInboxesMapList, err := peerSecretInboxesMapListDatastore.GetMapList()
if (err != nil) { return false, err }
addSecretInboxToPeerInboxesMapList := func(secretInboxSeed [22]byte, inboxStartTime int64, inboxEndTime int64)error{
secretInboxSeedHex := encoding.EncodeBytesToHexString(secretInboxSeed[:])
for _, inboxMap := range peerSecretInboxesMapList{
conversationMyIdentityHash, exists := inboxMap["MyIdentityHash"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing MyIdentityHash")
}
if (conversationMyIdentityHash != myIdentityHashString){
continue
}
conversationTheirIdentityHash, exists := inboxMap["TheirIdentityHash"]
if (exists == false) {
return errors.New("Malformed peerSecretInboxesMapList: Item missing TheirIdentityHash")
}
if (conversationTheirIdentityHash != peerIdentityHashString){
continue
}
conversationNetworkType, exists := inboxMap["NetworkType"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing NetworkType")
}
conversationNetworkTypeByte, err := helpers.ConvertNetworkTypeStringToByte(conversationNetworkType)
if (err != nil){
return errors.New("Malformed peerSecretInboxesMapList: Item contains invalid NetworkType: " + conversationNetworkType)
}
if (conversationNetworkTypeByte != networkType){
continue
}
currentSecretInboxSeed, exists := inboxMap["SecretInboxSeed"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing SecretInboxSeed")
}
if (currentSecretInboxSeed != secretInboxSeedHex){
continue
}
// An entry already exists for this inbox
// We will update it if needed
mostRecentSentTime, exists := inboxMap["MostRecentSentTime"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing MostRecentSentTime")
}
mostRecentSentTimeInt64, err := helpers.ConvertStringToInt64(mostRecentSentTime)
if (err != nil){
return errors.New("Malformed peerSecretInboxesMapList: Item contains invalid MostRecentSentTime: " + mostRecentSentTime)
}
if (mostRecentSentTimeInt64 < messageSentTime){
messageSentTimeString := helpers.ConvertInt64ToString(messageSentTime)
inboxMap["MostRecentSentTime"] = messageSentTimeString
}
existingInboxStartTimeString, exists := inboxMap["InboxStartTime"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing InboxStartTime")
}
existingInboxStartTime, err := helpers.ConvertStringToInt64(existingInboxStartTimeString)
if (err != nil){
return errors.New("Malformed peerSecretInboxesMapList: Item contains invalid InboxStartTime: " + existingInboxStartTimeString)
}
existingInboxEndTimeString, exists := inboxMap["InboxEndTime"]
if (exists == false){
return errors.New("Malformed peerSecretInboxesMapList: Item missing InboxEndTime")
}
existingInboxEndTime, err := helpers.ConvertStringToInt64(existingInboxEndTimeString)
if (err != nil){
return errors.New("Malformed peerSecretInboxesMapList: Item contains invalid InboxEndTime: " + existingInboxEndTimeString)
}
if (existingInboxStartTime <= inboxStartTime && existingInboxEndTime >= inboxEndTime){
// Existing inbox start/end times are valid
// Nothing else to change
return nil
}
// Start/end times must be expanded
newInboxStartTime := min(existingInboxStartTime, inboxStartTime)
newInboxEndTime := max(existingInboxEndTime, inboxEndTime)
newInboxStartTimeString := helpers.ConvertInt64ToString(newInboxStartTime)
newInboxEndTimeString := helpers.ConvertInt64ToString(newInboxEndTime)
inboxMap["InboxStartTime"] = newInboxStartTimeString
inboxMap["InboxEndTime"] = newInboxEndTimeString
return nil
}
// This is only reached if the inbox map does not exist.
networkTypeString := helpers.ConvertByteToString(networkType)
messageSentTimeString := helpers.ConvertInt64ToString(messageSentTime)
inboxStartTimeString := helpers.ConvertInt64ToString(inboxStartTime)
inboxEndTimeString := helpers.ConvertInt64ToString(inboxEndTime)
newInboxMap := map[string]string{
"MyIdentityHash": myIdentityHashString,
"TheirIdentityHash": peerIdentityHashString,
"NetworkType": networkTypeString,
"SecretInboxSeed": secretInboxSeedHex,
"MostRecentSentTime": messageSentTimeString,
"InboxStartTime": inboxStartTimeString,
"InboxEndTime": inboxEndTimeString,
}
peerSecretInboxesMapList = append(peerSecretInboxesMapList, newInboxMap)
return nil
}
err = addSecretInboxToPeerInboxesMapList(peerCurrentSecretInboxSeed, currentSecretInboxStartTime, currentSecretInboxEndTime)
if (err != nil) { return false, err }
err = addSecretInboxToPeerInboxesMapList(peerNextSecretInboxSeed, nextSecretInboxStartTime, nextSecretInboxEndTime)
if (err != nil) { return false, err }
err = peerSecretInboxesMapListDatastore.OverwriteMapList(peerSecretInboxesMapList)
if (err != nil) { return false, err }
return true, nil
}
//Outputs:
// -bool: Secret inbox found
// -[10]byte: Current Secret inbox
// -[32]byte: Current secret inbox sealer key
// -error
func GetPeerConversationCurrentSecretInbox(myIdentityHash [16]byte, peerIdentityHash [16]byte, networkType byte)(bool, [10]byte, [32]byte, error){
myIdentityHashString, myIdentityType, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
if (err != nil){
myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
return false, [10]byte{}, [32]byte{}, errors.New("GetPeerConversationCurrentSecretInbox called with invalid myIdentityHash: " + myIdentityHashHex)
}
peerIdentityHashString, peerIdentityType, err := identity.EncodeIdentityHashBytesToString(peerIdentityHash)
if (err != nil) {
peerIdentityHashHex := encoding.EncodeBytesToHexString(peerIdentityHash[:])
return false, [10]byte{}, [32]byte{}, errors.New("GetPeerConversationCurrentSecretInbox called with invalid peerIdentityHash: " + peerIdentityHashHex)
}
if (myIdentityType != peerIdentityType){
return false, [10]byte{}, [32]byte{}, errors.New("GetPeerConversationCurrentSecretInbox called with mismatched my/peer identityTypes.")
}
networkTypeIsValid := helpers.VerifyNetworkType(networkType)
if (networkTypeIsValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, [10]byte{}, [32]byte{}, errors.New("GetPeerConversationCurrentSecretInbox called with invalid networkType: " + networkTypeString)
}
// We find the newest available peer secret inbox seed for this conversation
// We first retrieve all peer secret inboxes for this conversation that exist on this network
networkTypeString := helpers.ConvertByteToString(networkType)
lookupMap := map[string]string{
"MyIdentityHash": myIdentityHashString,
"TheirIdentityHash": peerIdentityHashString,
"NetworkType": networkTypeString,
}
anyExist, peerSecretInboxesMapList, err := peerSecretInboxesMapListDatastore.GetMapListItems(lookupMap)
if (err != nil) { return false, [10]byte{}, [32]byte{}, err }
if (anyExist == false){
return false, [10]byte{}, [32]byte{}, nil
}
currentTime := time.Now().Unix()
anyValidInboxFound := false
var newestSecretInboxSeed [22]byte
newestSecretInboxMostRecentSentTime := int64(0)
for _, inboxMap := range peerSecretInboxesMapList{
inboxStartTimeString, exists := inboxMap["InboxStartTime"]
if (exists == false){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item missing InboxStartTime")
}
inboxStartTime, err := helpers.ConvertStringToInt64(inboxStartTimeString)
if (err != nil){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid inboxStartTime: " + inboxStartTimeString)
}
inboxEndTimeString, exists := inboxMap["InboxEndTime"]
if (exists == false){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item missing InboxEndTime")
}
inboxEndTime, err := helpers.ConvertStringToInt64(inboxEndTimeString)
if (err != nil){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid inboxEndTime: " + inboxEndTimeString)
}
if (inboxStartTime > currentTime || inboxEndTime < currentTime){
// Inbox is not valid right now. Skip it.
continue
}
secretInboxSeedString, exists := inboxMap["SecretInboxSeed"]
if (exists == false){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item missing SecretInboxSeed")
}
secretInboxSeedBytes, err := encoding.DecodeHexStringToBytes(secretInboxSeedString)
if (err != nil){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid SecretInboxSeed: Not Hex.")
}
if (len(secretInboxSeedBytes) != 22){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid SecretInboxSeed: Invalid length.")
}
secretInboxSeed := [22]byte(secretInboxSeedBytes)
inboxMostRecentSentTimeString, exists := inboxMap["MostRecentSentTime"]
if (exists == false){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item missing MostRecentSentTime")
}
inboxMostRecentSentTime, err := helpers.ConvertStringToInt64(inboxMostRecentSentTimeString)
if (err != nil){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid MostRecentSentTime: " + inboxMostRecentSentTimeString)
}
if (anyValidInboxFound == false || newestSecretInboxMostRecentSentTime < inboxMostRecentSentTime){
anyValidInboxFound = true
newestSecretInboxMostRecentSentTime = inboxMostRecentSentTime
newestSecretInboxSeed = secretInboxSeed
}
}
if (anyValidInboxFound == false){
return false, [10]byte{}, [32]byte{}, nil
}
// Now we check if their device identifier has changed after we received this secret inbox
deviceInfoFound, _, newestDeviceFirstSeenTime, err := peerDevices.GetPeerNewestDeviceInfo(peerIdentityHash, networkType)
if (err != nil) { return false, [10]byte{}, [32]byte{}, err }
if (deviceInfoFound == true && newestDeviceFirstSeenTime > newestSecretInboxMostRecentSentTime){
// The peer has changed their device after they sent us this secret inbox
// We assume this inbox was not carried over to the new device, and we say that no secret inbox exists
return false, [10]byte{}, [32]byte{}, nil
}
newestSecretInbox, currentSecretInboxSealerKey, err := inbox.GetSecretInboxAndSealerKeyFromSecretInboxSeed(newestSecretInboxSeed)
if (err != nil){
return false, [10]byte{}, [32]byte{}, errors.New("Malformed peerSecretInboxesMapList: Item contains invalid SecretInboxSeed.")
}
return true, newestSecretInbox, currentSecretInboxSealerKey, nil
}