355 lines
14 KiB
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
|
|
}
|
|
|
|
|