Files
triangles_v5/src/smessage.h
T
sami7777 32330b420e Replace boost::signals2 with homegrown CSignal<>
Drops the last boost::signals2 dependency from the GUI/wallet/smessage
notification path. CSignal<> is a std::function-based fan-out signal
with explicit Connection tokens (no equivalent-bind disconnect). Same
semantics for the void-returning case; non-void variant returns the
last-connected slot's result via std::optional.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 17:36:05 -07:00

390 lines
11 KiB
C++

// Copyright (c) 2014 The ShadowCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef SEC_MESSAGE_H
#define SEC_MESSAGE_H
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include "net.h"
#include "db.h"
#include "signal.h"
#include "wallet.h"
#include "lz4/lz4.h"
const unsigned int SMSG_HDR_LEN = 104; // length of unencrypted header, 4 + 2 + 1 + 8 + 16 + 33 + 32 + 4 +4
const unsigned int SMSG_PL_HDR_LEN = 1+20+65+4; // length of encrypted header in payload
const unsigned int SMSG_BUCKET_LEN = 60 * 10; // in seconds
const unsigned int SMSG_RETENTION = 60 * 60 * 48; // in seconds
const unsigned int SMSG_SEND_DELAY = 2; // in seconds, SecureMsgSendData will delay this long between firing
const unsigned int SMSG_THREAD_DELAY = 20;
const unsigned int SMSG_TIME_LEEWAY = 60;
const unsigned int SMSG_TIME_IGNORE = 90; // seconds that a peer is ignored for if they fail to deliver messages for a smsgWant
const unsigned int SMSG_MAX_MSG_BYTES = 4096; // the user input part
// max size of payload worst case compression
const unsigned int SMSG_MAX_MSG_WORST = LZ4_COMPRESSBOUND(SMSG_MAX_MSG_BYTES+SMSG_PL_HDR_LEN);
#define SMSG_MASK_UNREAD (1 << 0)
extern bool fSecMsgEnabled;
class SecMsgStored;
// Inbox db changed, called with lock cs_smsgDB held.
extern CSignal<void(SecMsgStored&)> NotifySecMsgInboxChanged;
// Outbox db changed, called with lock cs_smsgDB held.
extern CSignal<void(SecMsgStored&)> NotifySecMsgOutboxChanged;
// Wallet Unlocked, called after all messages received while locked have been processed.
extern CSignal<void()> NotifySecMsgWalletUnlocked;
class SecMsgBucket;
class SecMsgAddress;
class SecMsgOptions;
extern std::map<int64_t, SecMsgBucket> smsgBuckets;
extern std::vector<SecMsgAddress> smsgAddresses;
extern SecMsgOptions smsgOptions;
extern CCriticalSection cs_smsg; // all except inbox and outbox
extern CCriticalSection cs_smsgDB;
#pragma pack(push, 1)
class SecureMessage
{
public:
SecureMessage()
{
nPayload = 0;
pPayload = NULL;
};
~SecureMessage()
{
if (pPayload)
delete[] pPayload;
pPayload = NULL;
};
unsigned char hash[4];
unsigned char version[2];
unsigned char flags;
int64_t timestamp;
unsigned char iv[16];
unsigned char cpkR[33];
unsigned char mac[32];
unsigned char nonse[4];
uint32_t nPayload;
unsigned char* pPayload;
};
#pragma pack(pop)
class MessageData
{
// -- Decrypted SecureMessage data
public:
int64_t timestamp;
std::string sToAddress;
std::string sFromAddress;
std::vector<unsigned char> vchMessage; // null terminated plaintext
};
class SecMsgToken
{
public:
SecMsgToken(int64_t ts, unsigned char* p, int np, long int o, uint32_t nFile = 1)
{
timestamp = ts;
if (np < 8) // payload will always be > 8, just make sure
memset(sample, 0, 8);
else
memcpy(sample, p, 8);
fileIndex = nFile;
offset = o;
};
SecMsgToken()
{
timestamp = 0;
memset(sample, 0, 8);
fileIndex = 1;
offset = 0;
};
~SecMsgToken() {};
bool operator <(const SecMsgToken& y) const
{
// pack and memcmp from timesent?
if (timestamp == y.timestamp)
return memcmp(sample, y.sample, 8) < 0;
return timestamp < y.timestamp;
}
int64_t timestamp; // doesn't need to be full 64 bytes?
unsigned char sample[8]; // first 8 bytes of payload - a hash
uint32_t fileIndex; // rotated bucket file suffix, eg _02.dat
int64_t offset; // offset
};
class SecMsgBucket
{
public:
SecMsgBucket()
{
timeChanged = 0;
hash = 0;
nLockCount = 0;
nLockPeerId = 0;
};
~SecMsgBucket() {};
void hashBucket();
int64_t timeChanged;
uint32_t hash; // token set should get ordered the same on each node
uint32_t nLockCount; // set when smsgWant first sent, unset at end of smsgMsg, ticks down in ThreadSecureMsg()
uint32_t nLockPeerId; // id of peer that bucket is locked for
std::set<SecMsgToken> setTokens;
};
// -- get at the data
class CTrianglesAddress_B : public CTrianglesAddress
{
public:
unsigned char getVersion()
{
return nVersion;
}
};
class CKeyID_B : public CKeyID
{
public:
unsigned int* GetPPN()
{
return pn;
}
};
class SecMsgAddress
{
public:
SecMsgAddress() {};
SecMsgAddress(std::string sAddr, bool receiveOn, bool receiveAnon)
{
sAddress = sAddr;
fReceiveEnabled = receiveOn;
fReceiveAnon = receiveAnon;
};
std::string sAddress;
bool fReceiveEnabled;
bool fReceiveAnon;
IMPLEMENT_SERIALIZE
(
READWRITE(this->sAddress);
READWRITE(this->fReceiveEnabled);
READWRITE(this->fReceiveAnon);
);
};
class SecMsgOptions
{
public:
SecMsgOptions()
{
// -- default options
fNewAddressRecv = true;
fNewAddressAnon = true;
}
bool fNewAddressRecv;
bool fNewAddressAnon;
};
class SecMsgCrypter
{
private:
unsigned char chKey[32];
unsigned char chIV[16];
bool fKeySet;
public:
SecMsgCrypter()
{
// Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap)
// Note that this does nothing about suspend-to-disk (which will put all our key data on disk)
// Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process.
LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey);
LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV);
fKeySet = false;
}
~SecMsgCrypter()
{
// clean key
memset(&chKey, 0, sizeof chKey);
memset(&chIV, 0, sizeof chIV);
fKeySet = false;
LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey);
LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV);
}
bool SetKey(const std::vector<unsigned char>& vchNewKey, unsigned char* chNewIV);
bool SetKey(const unsigned char* chNewKey, unsigned char* chNewIV);
bool Encrypt(unsigned char* chPlaintext, uint32_t nPlain, std::vector<unsigned char> &vchCiphertext);
bool Decrypt(unsigned char* chCiphertext, uint32_t nCipher, std::vector<unsigned char>& vchPlaintext);
};
class SecMsgStored
{
public:
int64_t timeReceived;
char status; // read etc
uint16_t folderId;
std::string sAddrTo; // when in owned addr, when sent remote addr
std::string sAddrOutbox; // owned address this copy was encrypted with
std::vector<unsigned char> vchMessage; // message header + encryped payload
IMPLEMENT_SERIALIZE
(
READWRITE(this->timeReceived);
READWRITE(this->status);
READWRITE(this->folderId);
READWRITE(this->sAddrTo);
READWRITE(this->sAddrOutbox);
READWRITE(this->vchMessage);
);
};
class SecMsgDB
{
public:
SecMsgDB()
{
activeBatch = NULL;
};
~SecMsgDB()
{
// -- deletes only data scoped to this TxDB object.
if (activeBatch)
delete activeBatch;
};
bool Open(const char* pszMode="r+");
bool ScanBatch(const CDataStream& key, std::string* value, bool* deleted) const;
bool TxnBegin();
bool TxnCommit();
bool TxnAbort();
bool ReadPK(CKeyID& addr, CPubKey& pubkey);
bool WritePK(CKeyID& addr, CPubKey& pubkey);
bool ExistsPK(CKeyID& addr);
bool NextSmesg(leveldb::Iterator* it, std::string& prefix, unsigned char* vchKey, SecMsgStored& smsgStored);
bool NextSmesgKey(leveldb::Iterator* it, std::string& prefix, unsigned char* vchKey);
bool ReadSmesg(unsigned char* chKey, SecMsgStored& smsgStored);
bool WriteSmesg(unsigned char* chKey, SecMsgStored& smsgStored);
bool ExistsSmesg(unsigned char* chKey);
bool EraseSmesg(unsigned char* chKey);
leveldb::DB *pdb; // points to the global instance
leveldb::WriteBatch *activeBatch;
};
std::string getTimeString(int64_t timestamp, char *buffer, size_t nBuffer);
std::string fsReadable(uint64_t nBytes);
int SecureMsgBuildBucketSet();
int SecureMsgAddWalletAddresses();
int SecureMsgReadIni();
int SecureMsgWriteIni();
bool SecureMsgStart(bool fDontStart, bool fScanChain);
bool SecureMsgShutdown();
bool SecureMsgEnable();
bool SecureMsgDisable();
bool SecureMsgReceiveData(CNode* pfrom, std::string strCommand, CDataStream& vRecv);
bool SecureMsgSendData(CNode* pto, bool fSendTrickle);
bool SecureMsgScanBlock(CBlock& block);
bool ScanChainForPublicKeys(CBlockIndex* pindexStart);
bool SecureMsgScanBlockChain();
bool SecureMsgScanBuckets();
int SecureMsgWalletUnlocked();
int SecureMsgWalletKeyChanged(std::string sAddress, std::string sLabel, ChangeType mode);
int SecureMsgScanMessage(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload, bool reportToGui);
int SecureMsgGetStoredKey(CKeyID& ckid, CPubKey& cpkOut);
int SecureMsgGetLocalKey(CKeyID& ckid, CPubKey& cpkOut);
int SecureMsgGetLocalPublicKey(std::string& strAddress, std::string& strPublicKey);
int SecureMsgAddAddress(std::string& address, std::string& publicKey);
int SecureMsgRetrieve(SecMsgToken &token, std::vector<unsigned char>& vchData);
int SecureMsgReceive(CNode* pfrom, std::vector<unsigned char>& vchData);
int SecureMsgStoreUnscanned(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload);
int SecureMsgStore(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload, bool fUpdateBucket);
int SecureMsgStore(SecureMessage& smsg, bool fUpdateBucket);
int SecureMsgSend(std::string& addressFrom, std::string& addressTo, std::string& message, std::string& sError);
int SecureMsgValidate(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload);
int SecureMsgSetHash(unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload);
int SecureMsgEncrypt(SecureMessage& smsg, std::string& addressFrom, std::string& addressTo, std::string& message);
int SecureMsgDecrypt(bool fTestOnly, std::string& address, unsigned char *pHeader, unsigned char *pPayload, uint32_t nPayload, MessageData& msg);
int SecureMsgDecrypt(bool fTestOnly, std::string& address, SecureMessage& smsg, MessageData& msg);
#endif // SEC_MESSAGE_H