i2pd/libi2pd/ECIESX25519AEADRatchetSession.h

255 lines
9.6 KiB
C
Raw Normal View History

/*
2024-01-30 20:41:57 +00:00
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2020-01-15 20:13:43 +00:00
#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__
#define ECIES_X25519_AEAD_RATCHET_SESSION_H__
#include <string.h>
2020-01-15 20:13:43 +00:00
#include <inttypes.h>
#include <functional>
2020-01-21 19:40:23 +00:00
#include <memory>
2020-01-17 16:21:41 +00:00
#include <vector>
2020-03-26 23:03:38 +00:00
#include <list>
2020-04-17 01:30:18 +00:00
#include <unordered_map>
2020-01-15 20:13:43 +00:00
#include "Identity.h"
#include "Crypto.h"
#include "Garlic.h"
2020-04-17 01:30:18 +00:00
#include "Tag.h"
2020-01-15 20:13:43 +00:00
namespace i2p
{
namespace garlic
{
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after
const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
2021-01-09 23:59:09 +00:00
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
2020-05-01 01:27:35 +00:00
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
2020-05-01 01:27:35 +00:00
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
2020-12-24 19:06:34 +00:00
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
2020-05-01 01:27:35 +00:00
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
// - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */
2021-01-04 23:20:16 +00:00
class RatchetTagSet
{
public:
2021-01-04 23:20:16 +00:00
RatchetTagSet () {};
virtual ~RatchetTagSet () {};
void DHInitialize (const uint8_t * rootKey, const uint8_t * k);
void NextSessionTagRatchet ();
uint64_t GetNextSessionTag ();
2020-04-25 01:36:08 +00:00
const uint8_t * GetNextRootKey () const { return m_NextRootKey; };
int GetNextIndex () const { return m_NextIndex; };
2020-03-07 23:46:40 +00:00
void GetSymmKey (int index, uint8_t * key);
2020-09-08 11:46:55 +00:00
void DeleteSymmKey (int index);
2020-04-24 19:46:02 +00:00
2020-04-25 01:36:08 +00:00
int GetTagSetID () const { return m_TagSetID; };
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
2020-02-09 02:51:02 +00:00
private:
2021-08-01 22:42:13 +00:00
i2p::data::Tag<64> m_SessionTagKeyData;
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
2021-01-04 23:20:16 +00:00
int m_NextIndex, m_NextSymmKeyIndex;
std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
int m_TagSetID = 0;
};
2021-01-04 23:20:16 +00:00
class ECIESX25519AEADRatchetSession;
class ReceiveRatchetTagSet: public RatchetTagSet,
public std::enable_shared_from_this<ReceiveRatchetTagSet>
{
public:
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
m_Session (session), m_IsNS (isNS) {};
2021-01-04 23:20:16 +00:00
bool IsNS () const { return m_IsNS; };
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
2021-05-11 00:20:25 +00:00
int GetTrimBehind () const { return m_TrimBehindIndex; };
void Expire ();
bool IsExpired (uint64_t ts) const;
virtual bool IsIndexExpired (int index) const;
2021-01-04 23:20:16 +00:00
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
2021-01-04 23:20:16 +00:00
private:
2021-01-04 23:20:16 +00:00
int m_TrimBehindIndex = 0;
std::shared_ptr<ECIESX25519AEADRatchetSession> m_Session;
bool m_IsNS;
uint64_t m_ExpirationTimestamp = 0;
};
2020-09-15 23:39:18 +00:00
class SymmetricKeyTagSet: public ReceiveRatchetTagSet
2020-09-15 23:39:18 +00:00
{
public:
SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key);
2020-09-15 23:39:18 +00:00
bool IsIndexExpired (int index) const { return false; };
bool HandleNextMessage (uint8_t * buf, size_t len, int index);
2020-09-15 23:39:18 +00:00
private:
GarlicDestination * m_Destination;
uint8_t m_Key[32];
};
enum ECIESx25519BlockType
2020-01-15 20:13:43 +00:00
{
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
2020-01-15 20:13:43 +00:00
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextKey = 7,
eECIESx25519BlkAck = 8,
eECIESx25519BlkAckRequest = 9,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
2020-01-15 20:13:43 +00:00
2020-04-25 01:36:08 +00:00
const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01;
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession,
private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
eSessionStateNew = 0,
eSessionStateNewSessionReceived,
2020-02-03 21:21:07 +00:00
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished,
2021-07-15 22:18:55 +00:00
eSessionStateOneTime
};
2020-01-17 16:21:41 +00:00
struct DHRatchet
{
int keyID = 0;
std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key
2020-04-26 23:27:31 +00:00
bool newKey = true;
};
2020-01-15 20:13:43 +00:00
public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS);
~ECIESX25519AEADRatchetSession ();
2020-01-15 20:13:43 +00:00
2021-01-04 23:20:16 +00:00
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
2021-07-15 22:18:55 +00:00
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
void Terminate () { m_IsTerminated = true; }
2020-01-30 16:48:32 +00:00
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
{
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
}
bool CheckExpired (uint64_t ts); // true is expired
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; };
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated () const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
private:
2020-01-15 20:13:43 +00:00
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
2020-01-15 20:13:43 +00:00
2020-02-03 21:21:07 +00:00
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
2021-01-04 23:20:16 +00:00
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
2020-02-05 20:48:51 +00:00
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
size_t CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
2020-05-13 22:09:26 +00:00
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
2021-01-04 23:20:16 +00:00
void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
2020-04-26 23:27:31 +00:00
void NewNextSendRatchet ();
2020-01-15 20:13:43 +00:00
private:
uint8_t m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
2020-04-28 22:23:13 +00:00
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
SessionState m_State = eSessionStateNew;
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
m_LastSentTimestamp = 0; // in milliseconds
2020-06-04 22:19:38 +00:00
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
2020-03-31 21:35:51 +00:00
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
2020-04-26 23:27:31 +00:00
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
2020-06-14 01:24:16 +00:00
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
public:
// for HTTP only
int GetState () const { return (int)m_State; }
i2p::data::IdentHash GetDestination () const
{
return m_Destination ? *m_Destination : i2p::data::IdentHash ();
}
};
// single session for all incoming messages
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
{
public:
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
bool HandleNextMessage (const uint8_t * buf, size_t len);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
private:
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
};
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
2024-01-30 20:41:57 +00:00
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<I2NPMessage> msg, const uint8_t * routerPublicKey);
2020-01-15 20:13:43 +00:00
}
}
#endif
2021-07-15 22:18:55 +00:00