mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-04-19 23:46:36 +02:00
Compare commits
104 commits
Author | SHA1 | Date | |
---|---|---|---|
|
2afdd5b723 | ||
|
9b967059ad | ||
|
58818514e7 | ||
|
bbfe81cb79 | ||
|
67fe6faf2d | ||
|
1c162f9fd5 | ||
|
e69b56c4e3 | ||
|
f6c93f7345 | ||
|
9c46ff2449 | ||
|
1a04b59585 | ||
|
828cd9d07b | ||
|
4c5d0116f8 | ||
|
459be02d18 | ||
|
a83cd99f58 | ||
|
9ab1a67f0b | ||
|
f6abbe5908 | ||
|
711f5bcc62 | ||
|
6b38085f27 | ||
|
3afe6455b2 | ||
|
2f2ecc32d2 | ||
|
bce0ccf161 | ||
|
8ca74a3e1d | ||
|
3be4c7217f | ||
|
78d97179b8 | ||
|
2280338900 | ||
|
ad3b999732 | ||
|
00920a049d | ||
|
567183647e | ||
|
c2f68d7021 | ||
|
7404ce7fd2 | ||
|
871fc14ba6 | ||
|
81ba19e1ae | ||
|
ecf19278e8 | ||
|
a40fa57ed4 | ||
|
d3cfbbd6b0 | ||
|
22d854a6be | ||
|
75d5c6036e | ||
|
a193186935 | ||
|
41197264c6 | ||
|
029e279b48 | ||
|
af5d2a415c | ||
|
7b98dd84d8 | ||
|
9fdbb14075 | ||
|
935c055a35 | ||
|
9769ab0a46 | ||
|
9684c86a69 | ||
|
b2fd30d042 | ||
|
9ce515ff79 | ||
|
46f530bfcd | ||
|
bd2b96627c | ||
|
609cd401bb | ||
|
bbf5c1655a | ||
|
8e6b9370d0 | ||
|
9cc625b19e | ||
|
93cc810f29 | ||
|
e0a21cf702 | ||
|
c2f6731296 | ||
|
706b9c51b1 | ||
|
c0b5f2d2ef | ||
|
d93a80cd2b | ||
|
960a85e415 | ||
|
67ab4fef6d | ||
|
2def747564 | ||
|
6a65680619 | ||
|
ede8244e54 | ||
|
c3d4d1bdf4 | ||
|
ad7ca428ae | ||
|
cd9427d5d5 | ||
|
e3227ee5ee | ||
|
dd58b2f867 | ||
|
972c6854bc | ||
|
4ddfe9c94c | ||
|
4e8b8465fa | ||
|
4d9b5e685d | ||
|
b500374f74 | ||
|
fe3e7b1f6e | ||
|
66a52a17c6 | ||
|
c113241ccd | ||
|
9c97909e04 | ||
|
c816d3e4cc | ||
|
4f82fe24da | ||
|
a1794ccd22 | ||
|
5f762845f0 | ||
|
539e7e988e | ||
|
2a4403f1e0 | ||
|
dcd15cc244 | ||
|
9432202fad | ||
|
bf050ac465 | ||
|
81dae1997d | ||
|
7e3d9649de | ||
|
9ba016259d | ||
|
7791b3952e | ||
|
b97f09cc95 | ||
|
aedf59d11a | ||
|
d09367d686 | ||
|
70f99ccc21 | ||
|
ef72ba3f34 | ||
|
251605e2b8 | ||
|
fa2178ca3e | ||
|
3d19fa12f6 | ||
|
48aaecacce | ||
|
4bb86b6a86 | ||
|
0588116489 | ||
|
78a37cc00f |
88 changed files with 2440 additions and 1169 deletions
8
.github/workflows/docker.yml
vendored
8
.github/workflows/docker.yml
vendored
|
@ -108,7 +108,7 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: purplei2p/i2pd:latest
|
||||
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
|
||||
- name: Create and push latest manifest image to GHCR
|
||||
|
@ -116,7 +116,7 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest
|
||||
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
|
||||
- name: Store release version to env
|
||||
|
@ -128,7 +128,7 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
|
||||
- name: Create and push release manifest to GHCR
|
||||
|
@ -136,5 +136,5 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
push: true
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
|
||||
All rights reserved.
|
||||
|
||||
|
|
3
Makefile
3
Makefile
|
@ -69,6 +69,9 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
|||
else ifneq (, $(findstring haiku, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.haiku
|
||||
else ifneq (, $(findstring solaris, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.solaris
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
ifeq ($(shell $(CXX) -dumpmachine | cut -c 1-4), i586)
|
||||
CXX = g++-x86
|
||||
else
|
||||
CXX = g++
|
||||
CXXFLAGS := -Wall -std=c++17
|
||||
endif
|
||||
CXXFLAGS := -Wall -std=c++20
|
||||
INCFLAGS = -I/system/develop/headers
|
||||
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||
LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_system -lboost_program_options -lpthread
|
||||
LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_program_options -lpthread
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
DEFINES += -DUSE_UPNP
|
||||
|
|
9
Makefile.solaris
Normal file
9
Makefile.solaris
Normal file
|
@ -0,0 +1,9 @@
|
|||
CXX = g++
|
||||
INCFLAGS = -I/usr/openssl/3/include
|
||||
CXXFLAGS := -Wall -std=c++20
|
||||
LDLIBS = -L/usr/openssl/3/lib/64 -lssl -lcrypto -lboost_program_options -lz -lpthread -lsocket
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
DEFINES += -DUSE_UPNP
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
|
@ -1,7 +1,8 @@
|
|||
[Unit]
|
||||
Description=I2P Router written in C++
|
||||
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
After=network.target network-online.target
|
||||
|
||||
[Service]
|
||||
User=i2pd
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -132,7 +132,8 @@ namespace http {
|
|||
|
||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
|
||||
{
|
||||
std::string state, stateText;
|
||||
std::string state;
|
||||
std::string_view stateText;
|
||||
switch (eState)
|
||||
{
|
||||
case i2p::tunnel::eTunnelStateBuildReplyReceived :
|
||||
|
@ -146,7 +147,7 @@ namespace http {
|
|||
}
|
||||
if (stateText.empty ()) stateText = tr(state);
|
||||
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + std::string(tr("exploratory")) + ")" : "") << "</span>, "; // TODO:
|
||||
ShowTraffic(s, bytes);
|
||||
s << "\r\n";
|
||||
}
|
||||
|
@ -213,7 +214,7 @@ namespace http {
|
|||
"</html>\r\n";
|
||||
}
|
||||
|
||||
static void ShowError(std::stringstream& s, const std::string& string)
|
||||
static void ShowError(std::stringstream& s, std::string_view string)
|
||||
{
|
||||
s << "<b>" << tr("ERROR") << ":</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
@ -1262,7 +1263,7 @@ namespace http {
|
|||
ShowLeasesSets(s);
|
||||
else {
|
||||
res.code = 400;
|
||||
ShowError(s, tr("Unknown page") + ": " + page);
|
||||
ShowError(s, std::string (tr("Unknown page")) + ": " + page); // TODO
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1418,13 +1419,11 @@ namespace http {
|
|||
{
|
||||
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
|
||||
uint8_t * signature = new uint8_t[signatureLen];
|
||||
char * sig = new char[signatureLen*2];
|
||||
std::stringstream out;
|
||||
|
||||
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
|
||||
dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature);
|
||||
auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
|
||||
sig[len] = 0;
|
||||
auto sig = i2p::data::ByteStreamToBase64 (signature, signatureLen);
|
||||
out << "#!sig=" << sig;
|
||||
s << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
|
||||
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
|
||||
|
@ -1433,7 +1432,6 @@ namespace http {
|
|||
"<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
|
||||
"</form>\r\n<br>\r\n";
|
||||
delete[] signature;
|
||||
delete[] sig;
|
||||
}
|
||||
else
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
|
||||
|
@ -1462,7 +1460,7 @@ namespace http {
|
|||
else
|
||||
{
|
||||
res.code = 400;
|
||||
ShowError(s, tr("Unknown command") + ": " + cmd);
|
||||
ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -15,7 +15,6 @@
|
|||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
|
@ -30,11 +29,24 @@ namespace i2p
|
|||
namespace client
|
||||
{
|
||||
I2PControlService::I2PControlService (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)),
|
||||
m_IsRunning (false),
|
||||
m_SSLContext (boost::asio::ssl::context::sslv23),
|
||||
m_ShutdownTimer (m_Service)
|
||||
{
|
||||
if (port)
|
||||
m_Acceptor = std::make_unique<boost::asio::ip::tcp::acceptor>(m_Service,
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port));
|
||||
else
|
||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
||||
{
|
||||
std::remove (address.c_str ()); // just in case
|
||||
m_LocalAcceptor = std::make_unique<boost::asio::local::stream_protocol::acceptor>(m_Service,
|
||||
boost::asio::local::stream_protocol::endpoint(address));
|
||||
}
|
||||
#else
|
||||
LogPrint(eLogError, "I2PControl: Local sockets are not supported");
|
||||
#endif
|
||||
|
||||
i2p::config::GetOption("i2pcontrol.password", m_Password);
|
||||
|
||||
// certificate / keys
|
||||
|
@ -98,7 +110,7 @@ namespace client
|
|||
{
|
||||
Accept ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
|
||||
m_Thread = std::make_unique<std::thread>(std::bind (&I2PControlService::Run, this));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,12 +119,19 @@ namespace client
|
|||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.cancel ();
|
||||
if (m_Acceptor) m_Acceptor->cancel ();
|
||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
||||
if (m_LocalAcceptor)
|
||||
{
|
||||
auto path = m_LocalAcceptor->local_endpoint().path();
|
||||
m_LocalAcceptor->cancel ();
|
||||
std::remove (path.c_str ());
|
||||
}
|
||||
#endif
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -134,40 +153,60 @@ namespace client
|
|||
|
||||
void I2PControlService::Accept ()
|
||||
{
|
||||
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
|
||||
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
if (m_Acceptor)
|
||||
{
|
||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > (m_Service, m_SSLContext);
|
||||
m_Acceptor->async_accept (newSocket->lowest_layer(),
|
||||
[this, newSocket](const boost::system::error_code& ecode)
|
||||
{
|
||||
HandleAccepted (ecode, newSocket);
|
||||
});
|
||||
}
|
||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
||||
else if (m_LocalAcceptor)
|
||||
{
|
||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::local::stream_protocol::socket> > (m_Service, m_SSLContext);
|
||||
m_LocalAcceptor->async_accept (newSocket->lowest_layer(),
|
||||
[this, newSocket](const boost::system::error_code& ecode)
|
||||
{
|
||||
HandleAccepted (ecode, newSocket);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::HandleAccepted (const boost::system::error_code& ecode,
|
||||
std::shared_ptr<ssl_socket> newSocket)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
|
||||
if (ecode) {
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (socket);
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (newSocket);
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
||||
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
||||
ReadRequest (socket);
|
||||
[this, socket](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
ReadRequest (socket);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
auto request = std::make_shared<I2PControlBuffer>();
|
||||
|
@ -177,10 +216,13 @@ namespace client
|
|||
#else
|
||||
boost::asio::buffer (request->data (), request->size ()),
|
||||
#endif
|
||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, request));
|
||||
[this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred)
|
||||
{
|
||||
HandleRequestReceived (ecode, bytes_transferred, socket, request);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf)
|
||||
|
@ -258,6 +300,7 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
|
@ -267,7 +310,7 @@ namespace client
|
|||
std::ostringstream header;
|
||||
header << "HTTP/1.1 200 OK\r\n";
|
||||
header << "Connection: close\r\n";
|
||||
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
||||
header << "Content-Length: " << std::to_string(len) << "\r\n";
|
||||
header << "Content-Type: application/json\r\n";
|
||||
header << "Date: ";
|
||||
std::time_t t = std::time (nullptr);
|
||||
|
@ -280,16 +323,11 @@ namespace client
|
|||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
||||
boost::asio::transfer_all (),
|
||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
|
||||
}
|
||||
[socket, buf](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
|
||||
});
|
||||
}
|
||||
|
||||
// handlers
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -35,8 +35,6 @@ namespace client
|
|||
|
||||
class I2PControlService: public I2PControlHandlers
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
public:
|
||||
|
||||
I2PControlService (const std::string& address, int port);
|
||||
|
@ -49,16 +47,18 @@ namespace client
|
|||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
template<typename ssl_socket>
|
||||
void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> newSocket);
|
||||
template<typename ssl_socket>
|
||||
void Handshake (std::shared_ptr<ssl_socket> socket);
|
||||
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
template<typename ssl_socket>
|
||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||
template<typename ssl_socket>
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
template<typename ssl_socket>
|
||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
||||
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
|
||||
void CreateCertificate (const char *crt_path, const char *key_path);
|
||||
|
||||
|
@ -86,10 +86,13 @@ namespace client
|
|||
|
||||
std::string m_Password;
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
|
||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
||||
std::unique_ptr<boost::asio::local::stream_protocol::acceptor> m_LocalAcceptor;
|
||||
#endif
|
||||
boost::asio::ssl::context m_SSLContext;
|
||||
boost::asio::deadline_timer m_ShutdownTimer;
|
||||
std::set<std::string> m_Tokens;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace afrikaans // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"failed", "Het misluk"},
|
||||
{"unknown", "onbekend"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace armenian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace chinese // language namespace
|
|||
return 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace czech // language namespace
|
|||
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -30,7 +30,7 @@ namespace english // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"", ""},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace french // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f Kio"},
|
||||
{"%.2f MiB", "%.2f Mio"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace german // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -30,12 +30,12 @@ namespace i18n
|
|||
}
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg)
|
||||
std::string_view translate (std::string_view arg)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||
}
|
||||
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int n)
|
||||
{
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
|
|
24
i18n/I18N.h
24
i18n/I18N.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -10,6 +10,7 @@
|
|||
#define __I18N_H__
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
@ -18,12 +19,13 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
typedef std::map<std::string_view, std::string_view> LocaleStrings;
|
||||
class Locale
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const LocaleStrings& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
@ -34,7 +36,7 @@ namespace i18n
|
|||
return m_Language;
|
||||
}
|
||||
|
||||
std::string GetString (const std::string& arg) const
|
||||
std::string_view GetString (std::string_view arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
|
@ -47,7 +49,7 @@ namespace i18n
|
|||
}
|
||||
}
|
||||
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||
std::string GetPlural (const std::string& arg, const std::string& arg2, int n) const
|
||||
{
|
||||
const auto it = m_Plurals.find(arg2);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
|
@ -63,14 +65,14 @@ namespace i18n
|
|||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const LocaleStrings m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
void SetLanguage(const std::string &lang);
|
||||
std::string translate (const std::string& arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n);
|
||||
std::string_view translate (std::string_view arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, int n);
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
||||
|
@ -79,7 +81,7 @@ namespace i18n
|
|||
* @param arg String with message
|
||||
*/
|
||||
template<typename TValue>
|
||||
std::string tr (TValue&& arg)
|
||||
std::string_view tr (TValue&& arg)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
}
|
||||
|
@ -92,7 +94,7 @@ std::string tr (TValue&& arg)
|
|||
template<typename TValue, typename... TArgs>
|
||||
std::string tr (TValue&& arg, TArgs&&... args)
|
||||
{
|
||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
std::string tr_str = std::string (i2p::i18n::translate(std::forward<TValue>(arg))); // TODO:
|
||||
|
||||
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
std::string str(size, 0);
|
||||
|
@ -108,7 +110,7 @@ std::string tr (TValue&& arg, TArgs&&... args)
|
|||
* @param n Integer, used for selection of form
|
||||
*/
|
||||
template<typename TValue, typename TValue2>
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
|
|||
* @param args Array of arguments for string formatting
|
||||
*/
|
||||
template<typename TValue, typename TValue2, typename... TArgs>
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args)
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int n, TArgs&&... args)
|
||||
{
|
||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace italian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace polish // language namespace
|
|||
return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace portuguese // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace russian // language namespace
|
|||
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КиБ"},
|
||||
{"%.2f MiB", "%.2f МиБ"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace spanish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, The PurpleI2P Project
|
||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace swedish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, The PurpleI2P Project
|
||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace turkish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace turkmen // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace ukrainian // language namespace
|
|||
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КіБ"},
|
||||
{"%.2f MiB", "%.2f МіБ"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ namespace uzbek // language namespace
|
|||
return n > 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> strings
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
203
libi2pd/Base.cpp
203
libi2pd/Base.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -15,7 +15,7 @@ namespace i2p
|
|||
{
|
||||
namespace data
|
||||
{
|
||||
static const char T32[32] =
|
||||
static constexpr char T32[32] =
|
||||
{
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
|
@ -27,11 +27,6 @@ namespace data
|
|||
{
|
||||
return T32;
|
||||
}
|
||||
|
||||
bool IsBase32 (char ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||
}
|
||||
|
||||
static void iT64Build(void);
|
||||
|
||||
|
@ -43,7 +38,7 @@ namespace data
|
|||
* Direct Substitution Table
|
||||
*/
|
||||
|
||||
static const char T64[64] =
|
||||
static constexpr char T64[64] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
|
@ -59,24 +54,17 @@ namespace data
|
|||
{
|
||||
return T64;
|
||||
}
|
||||
|
||||
bool IsBase64 (char ch)
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~';
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse Substitution Table (built in run time)
|
||||
*/
|
||||
|
||||
static char iT64[256];
|
||||
static int isFirstTime = 1;
|
||||
|
||||
/*
|
||||
* Padding
|
||||
*/
|
||||
|
||||
static char P64 = '=';
|
||||
static constexpr char P64 = '=';
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -86,134 +74,112 @@ namespace data
|
|||
* Converts binary encoded data to BASE64 format.
|
||||
*
|
||||
*/
|
||||
|
||||
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */
|
||||
const uint8_t * InBuffer, /* Input buffer, binary data */
|
||||
size_t InCount, /* Number of bytes in the input buffer */
|
||||
char * OutBuffer, /* output buffer */
|
||||
size_t len /* length of output buffer */
|
||||
std::string ByteStreamToBase64 (// base64 encoded string
|
||||
const uint8_t * InBuffer, // Input buffer, binary data
|
||||
size_t InCount // Number of bytes in the input buffer
|
||||
)
|
||||
{
|
||||
unsigned char * ps;
|
||||
unsigned char * pd;
|
||||
unsigned char acc_1;
|
||||
unsigned char acc_2;
|
||||
int i;
|
||||
int n;
|
||||
int m;
|
||||
size_t outCount;
|
||||
|
||||
ps = (unsigned char *)InBuffer;
|
||||
n = InCount / 3;
|
||||
m = InCount % 3;
|
||||
if (!m)
|
||||
outCount = 4 * n;
|
||||
else
|
||||
outCount = 4 * (n + 1);
|
||||
size_t outCount = m ? (4 * (n + 1)) : (4 * n);
|
||||
|
||||
if (outCount > len) return 0;
|
||||
|
||||
pd = (unsigned char *)OutBuffer;
|
||||
std::string out;
|
||||
out.reserve (outCount);
|
||||
for ( i = 0; i < n; i++ )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x30;
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_1 = *ps++;
|
||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||
*pd++ = T64[acc_2];
|
||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
||||
out.push_back (T64[acc_2]);
|
||||
acc_1 &= 0x0f;
|
||||
acc_1 <<= 2;
|
||||
acc_2 = *ps++;
|
||||
acc_1 |= acc_2 >> 6; /* base64 digit #3 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_2 &= 0x3f; /* base64 digit #4 */
|
||||
*pd++ = T64[acc_2];
|
||||
acc_1 |= acc_2 >> 6; // base64 digit #3
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_2 &= 0x3f; // base64 digit #4
|
||||
out.push_back (T64[acc_2]);
|
||||
}
|
||||
if ( m == 1 )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
*pd++ = T64[acc_2];
|
||||
*pd++ = P64;
|
||||
*pd++ = P64;
|
||||
acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
out.push_back (T64[acc_2]);
|
||||
out.push_back (P64);
|
||||
out.push_back (P64);
|
||||
|
||||
}
|
||||
else if ( m == 2 )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x3f;
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_1 = *ps++;
|
||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||
*pd++ = T64[acc_2];
|
||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
||||
out.push_back (T64[acc_2]);
|
||||
acc_1 &= 0x0f;
|
||||
acc_1 <<= 2; /* base64 digit #3 */
|
||||
*pd++ = T64[acc_1];
|
||||
*pd++ = P64;
|
||||
acc_1 <<= 2; // base64 digit #3
|
||||
out.push_back (T64[acc_1]);
|
||||
out.push_back (P64);
|
||||
}
|
||||
|
||||
return outCount;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Base64ToByteStream
|
||||
* ------------------
|
||||
*
|
||||
* Converts BASE64 encoded data to binary format. If input buffer is
|
||||
* Converts BASE64 encoded string to binary format. If input buffer is
|
||||
* not properly padded, buffer of negative length is returned
|
||||
*
|
||||
*/
|
||||
|
||||
size_t Base64ToByteStream ( /* Number of output bytes */
|
||||
const char * InBuffer, /* BASE64 encoded buffer */
|
||||
size_t InCount, /* Number of input bytes */
|
||||
uint8_t * OutBuffer, /* output buffer length */
|
||||
size_t len /* length of output buffer */
|
||||
size_t Base64ToByteStream ( // Number of output bytes
|
||||
std::string_view base64Str, // BASE64 encoded string
|
||||
uint8_t * OutBuffer, // output buffer length
|
||||
size_t len // length of output buffer
|
||||
)
|
||||
{
|
||||
unsigned char * ps;
|
||||
unsigned char * pd;
|
||||
unsigned char acc_1;
|
||||
unsigned char acc_2;
|
||||
int i;
|
||||
int n;
|
||||
int m;
|
||||
size_t outCount;
|
||||
|
||||
if (isFirstTime)
|
||||
iT64Build();
|
||||
|
||||
n = InCount / 4;
|
||||
m = InCount % 4;
|
||||
|
||||
if (InCount && !m)
|
||||
outCount = 3 * n;
|
||||
if (base64Str.empty () || base64Str[0] == P64) return 0;
|
||||
auto d = std::div (base64Str.length (), 4);
|
||||
if (!d.rem)
|
||||
outCount = 3 * d.quot;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if(*InBuffer == P64)
|
||||
return 0;
|
||||
|
||||
ps = (unsigned char *)(InBuffer + InCount - 1);
|
||||
while ( *ps-- == P64 )
|
||||
outCount--;
|
||||
ps = (unsigned char *)InBuffer;
|
||||
|
||||
if (outCount > len)
|
||||
return 0;
|
||||
if (isFirstTime) iT64Build();
|
||||
|
||||
auto pos = base64Str.find_last_not_of (P64);
|
||||
if (pos == base64Str.npos) return 0;
|
||||
outCount -= (base64Str.length () - pos - 1);
|
||||
if (outCount > len) return 0;
|
||||
|
||||
auto ps = base64Str.begin ();
|
||||
pd = OutBuffer;
|
||||
auto endOfOutBuffer = OutBuffer + outCount;
|
||||
for ( i = 0; i < n; i++ )
|
||||
for (int i = 0; i < d.quot; i++)
|
||||
{
|
||||
acc_1 = iT64[*ps++];
|
||||
acc_2 = iT64[*ps++];
|
||||
acc_1 = iT64[int(*ps++)];
|
||||
acc_2 = iT64[int(*ps++)];
|
||||
acc_1 <<= 2;
|
||||
acc_1 |= acc_2 >> 4;
|
||||
*pd++ = acc_1;
|
||||
|
@ -221,45 +187,30 @@ namespace data
|
|||
break;
|
||||
|
||||
acc_2 <<= 4;
|
||||
acc_1 = iT64[*ps++];
|
||||
acc_1 = iT64[int(*ps++)];
|
||||
acc_2 |= acc_1 >> 2;
|
||||
*pd++ = acc_2;
|
||||
if (pd >= endOfOutBuffer)
|
||||
break;
|
||||
|
||||
acc_2 = iT64[*ps++];
|
||||
acc_2 = iT64[int(*ps++)];
|
||||
acc_2 |= acc_1 << 6;
|
||||
*pd++ = acc_2;
|
||||
}
|
||||
|
||||
return outCount;
|
||||
}
|
||||
|
||||
size_t Base64EncodingBufferSize (const size_t input_size)
|
||||
}
|
||||
|
||||
std::string ToBase64Standard (std::string_view in)
|
||||
{
|
||||
auto d = div (input_size, 3);
|
||||
if (d.rem)
|
||||
d.quot++;
|
||||
|
||||
return 4 * d.quot;
|
||||
}
|
||||
|
||||
std::string ToBase64Standard (const std::string& in)
|
||||
{
|
||||
auto len = Base64EncodingBufferSize (in.length ());
|
||||
char * str = new char[len + 1];
|
||||
auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len);
|
||||
str[l] = 0;
|
||||
auto str = ByteStreamToBase64 ((const uint8_t *)in.data (), in.length ());
|
||||
// replace '-' by '+' and '~' by '/'
|
||||
for (size_t i = 0; i < l; i++)
|
||||
if (str[i] == '-')
|
||||
str[i] = '+';
|
||||
else if (str[i] == '~')
|
||||
str[i] = '/';
|
||||
|
||||
std::string s(str);
|
||||
delete[] str;
|
||||
return s;
|
||||
for (auto& ch: str)
|
||||
if (ch == '-')
|
||||
ch = '+';
|
||||
else if (ch == '~')
|
||||
ch = '/';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -280,13 +231,12 @@ namespace data
|
|||
iT64[(int)P64] = 0;
|
||||
}
|
||||
|
||||
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen)
|
||||
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen)
|
||||
{
|
||||
unsigned int tmp = 0, bits = 0;
|
||||
size_t ret = 0;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
for (auto ch: base32Str)
|
||||
{
|
||||
char ch = inBuf[i];
|
||||
if (ch >= '2' && ch <= '7') // digit
|
||||
ch = (ch - '2') + 26; // 26 means a-z
|
||||
else if (ch >= 'a' && ch <= 'z')
|
||||
|
@ -306,13 +256,15 @@ namespace data
|
|||
tmp <<= 5;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen)
|
||||
}
|
||||
|
||||
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len)
|
||||
{
|
||||
size_t ret = 0, pos = 1;
|
||||
std::string out;
|
||||
out.reserve ((len * 8 + 4) / 5);
|
||||
size_t pos = 1;
|
||||
unsigned int bits = 8, tmp = inBuf[0];
|
||||
while (ret < outLen && (bits > 0 || pos < len))
|
||||
while (bits > 0 || pos < len)
|
||||
{
|
||||
if (bits < 5)
|
||||
{
|
||||
|
@ -332,10 +284,9 @@ namespace data
|
|||
|
||||
bits -= 5;
|
||||
int ind = (tmp >> bits) & 0x1F;
|
||||
outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2');
|
||||
ret++;
|
||||
out.push_back ((ind < 26) ? (ind + 'a') : ((ind - 26) + '2'));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -11,27 +11,42 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
std::string ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount);
|
||||
size_t Base64ToByteStream (std::string_view base64Str, uint8_t * OutBuffer, size_t len);
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
|
||||
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
|
||||
const char * GetBase32SubstitutionTable ();
|
||||
const char * GetBase64SubstitutionTable ();
|
||||
bool IsBase64 (char ch);
|
||||
constexpr bool IsBase64 (char ch)
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~';
|
||||
}
|
||||
|
||||
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
|
||||
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||
bool IsBase32 (char ch);
|
||||
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen);
|
||||
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len);
|
||||
constexpr bool IsBase32 (char ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
||||
*/
|
||||
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||
|
||||
std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization
|
||||
inline size_t Base64EncodingBufferSize(size_t input_size)
|
||||
{
|
||||
auto d = std::div (input_size, 3);
|
||||
if (d.rem) d.quot++;
|
||||
return 4 * d.quot;
|
||||
}
|
||||
|
||||
std::string ToBase64Standard (std::string_view in); // using standard table, for Proxy-Authorization
|
||||
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace data
|
|||
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
|
||||
{
|
||||
uint8_t addr[40]; // TODO: define length from b33
|
||||
size_t l = i2p::data::Base32ToByteStream (b33.data (), b33.length (), addr, 40);
|
||||
size_t l = i2p::data::Base32ToByteStream (b33, addr, 40);
|
||||
if (l < 32)
|
||||
{
|
||||
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
|
||||
|
@ -198,7 +198,7 @@ namespace data
|
|||
std::string BlindedPublicKey::ToB33 () const
|
||||
{
|
||||
if (m_PublicKey.size () > 32) return ""; // assume 25519
|
||||
uint8_t addr[35]; char str[60]; // TODO: define actual length
|
||||
uint8_t addr[35];
|
||||
uint8_t flags = 0;
|
||||
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
|
||||
addr[0] = flags; // flags
|
||||
|
@ -208,8 +208,7 @@ namespace data
|
|||
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
|
||||
// checksum is Little Endian
|
||||
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
|
||||
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60);
|
||||
return std::string (str, str + l);
|
||||
return ByteStreamToBase32 (addr, m_PublicKey.size () + 3);
|
||||
}
|
||||
|
||||
void BlindedPublicKey::GetCredential (uint8_t * credential) const
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#if OPENSSL_HKDF
|
||||
#include <openssl/kdf.h>
|
||||
#endif
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
#include <openssl/param_build.h>
|
||||
#include <openssl/core_names.h>
|
||||
#endif
|
||||
#include "CPU.h"
|
||||
#include "Crypto.h"
|
||||
#include "Ed25519.h"
|
||||
|
@ -29,7 +33,7 @@ namespace i2p
|
|||
{
|
||||
namespace crypto
|
||||
{
|
||||
const uint8_t elgp_[256]=
|
||||
constexpr uint8_t elgp_[256]=
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||
|
@ -49,9 +53,9 @@ namespace crypto
|
|||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
const int elgg_ = 2;
|
||||
constexpr int elgg_ = 2;
|
||||
|
||||
const uint8_t dsap_[128]=
|
||||
constexpr uint8_t dsap_[128]=
|
||||
{
|
||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
||||
|
@ -63,13 +67,13 @@ namespace crypto
|
|||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
||||
};
|
||||
|
||||
const uint8_t dsaq_[20]=
|
||||
constexpr uint8_t dsaq_[20]=
|
||||
{
|
||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
||||
0x68, 0x40, 0x46, 0xb7
|
||||
};
|
||||
|
||||
const uint8_t dsag_[128]=
|
||||
constexpr uint8_t dsag_[128]=
|
||||
{
|
||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
||||
|
@ -81,7 +85,7 @@ namespace crypto
|
|||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
||||
};
|
||||
|
||||
const int rsae_ = 65537;
|
||||
constexpr int rsae_ = 65537;
|
||||
|
||||
struct CryptoConstants
|
||||
{
|
||||
|
@ -146,6 +150,37 @@ namespace crypto
|
|||
#define dsap GetCryptoConstants ().dsap
|
||||
#define dsaq GetCryptoConstants ().dsaq
|
||||
#define dsag GetCryptoConstants ().dsag
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * CreateDSA (BIGNUM * pubKey, BIGNUM * privKey)
|
||||
{
|
||||
EVP_PKEY * pkey = nullptr;
|
||||
int selection = EVP_PKEY_KEY_PARAMETERS;
|
||||
auto bld = OSSL_PARAM_BLD_new();
|
||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, dsap);
|
||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, dsaq);
|
||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, dsag);
|
||||
if (pubKey)
|
||||
{
|
||||
OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PUB_KEY, pubKey);
|
||||
selection = EVP_PKEY_PUBLIC_KEY;
|
||||
}
|
||||
if (privKey)
|
||||
{
|
||||
OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PRIV_KEY, privKey);
|
||||
selection = EVP_PKEY_KEYPAIR;
|
||||
}
|
||||
auto params = OSSL_PARAM_BLD_to_param(bld);
|
||||
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "DSA", NULL);
|
||||
EVP_PKEY_fromdata_init(ctx);
|
||||
EVP_PKEY_fromdata(ctx, &pkey, selection, params);
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
OSSL_PARAM_free(params);
|
||||
OSSL_PARAM_BLD_free(bld);
|
||||
return pkey;
|
||||
}
|
||||
#else
|
||||
DSA * CreateDSA ()
|
||||
{
|
||||
DSA * dsa = DSA_new ();
|
||||
|
@ -153,7 +188,8 @@ namespace crypto
|
|||
DSA_set0_key (dsa, NULL, NULL);
|
||||
return dsa;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// DH/ElGamal
|
||||
|
||||
#if !IS_X86_64
|
||||
|
@ -785,6 +821,18 @@ namespace crypto
|
|||
|
||||
// Noise
|
||||
|
||||
void NoiseSymmetricState::Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub)
|
||||
{
|
||||
// pub is Bob's public static key, hh = SHA256(h)
|
||||
memcpy (m_CK, ck, 32);
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, hh, 32);
|
||||
SHA256_Update (&ctx, pub, 32);
|
||||
SHA256_Final (m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
|
||||
m_N = 0;
|
||||
}
|
||||
|
||||
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
|
@ -808,76 +856,95 @@ namespace crypto
|
|||
{
|
||||
HKDF (m_CK, sharedSecret, 32, "", m_CK);
|
||||
// new ck is m_CK[0:31], key is m_CK[32:63]
|
||||
m_N = 0;
|
||||
}
|
||||
|
||||
static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck,
|
||||
const uint8_t * hh, const uint8_t * pub)
|
||||
bool NoiseSymmetricState::Encrypt (const uint8_t * in, uint8_t * out, size_t len)
|
||||
{
|
||||
// pub is Bob's public static key, hh = SHA256(h)
|
||||
memcpy (state.m_CK, ck, 32);
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, hh, 32);
|
||||
SHA256_Update (&ctx, pub, 32);
|
||||
SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
|
||||
uint8_t nonce[12];
|
||||
if (m_N)
|
||||
{
|
||||
memset (nonce, 0, 4);
|
||||
htole64buf (nonce + 4, m_N);
|
||||
}
|
||||
else
|
||||
memset (nonce, 0, 12);
|
||||
auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len + 16, true);
|
||||
if (ret) m_N++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool NoiseSymmetricState::Decrypt (const uint8_t * in, uint8_t * out, size_t len)
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
if (m_N)
|
||||
{
|
||||
memset (nonce, 0, 4);
|
||||
htole64buf (nonce + 4, m_N);
|
||||
}
|
||||
else
|
||||
memset (nonce, 0, 12);
|
||||
auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len, false);
|
||||
if (ret) m_N++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||
{
|
||||
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
|
||||
static const uint8_t hh[32] =
|
||||
static constexpr char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
|
||||
static constexpr uint8_t hh[32] =
|
||||
{
|
||||
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
|
||||
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
|
||||
}; // hh = SHA256(protocol_name || 0)
|
||||
InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0
|
||||
state.Init ((const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0
|
||||
}
|
||||
|
||||
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||
{
|
||||
static const uint8_t protocolNameHash[32] =
|
||||
static constexpr uint8_t protocolNameHash[32] =
|
||||
{
|
||||
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed,
|
||||
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71
|
||||
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
|
||||
static const uint8_t hh[32] =
|
||||
static constexpr uint8_t hh[32] =
|
||||
{
|
||||
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5,
|
||||
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e
|
||||
}; // SHA256 (protocolNameHash)
|
||||
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||
state.Init (protocolNameHash, hh, pub);
|
||||
}
|
||||
|
||||
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub)
|
||||
{
|
||||
static const uint8_t protocolNameHash[32] =
|
||||
static constexpr uint8_t protocolNameHash[32] =
|
||||
{
|
||||
0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4,
|
||||
0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32
|
||||
}; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256")
|
||||
static const uint8_t hh[32] =
|
||||
static constexpr uint8_t hh[32] =
|
||||
{
|
||||
0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53,
|
||||
0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33
|
||||
}; // SHA256 (protocolNameHash)
|
||||
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||
state.Init (protocolNameHash, hh, pub);
|
||||
}
|
||||
|
||||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||
{
|
||||
static const uint8_t protocolNameHash[32] =
|
||||
static constexpr uint8_t protocolNameHash[32] =
|
||||
{
|
||||
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
|
||||
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
|
||||
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
|
||||
static const uint8_t hh[32] =
|
||||
static constexpr uint8_t hh[32] =
|
||||
{
|
||||
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
|
||||
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
|
||||
}; // SHA256 (protocolNameHash)
|
||||
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||
state.Init (protocolNameHash, hh, pub);
|
||||
}
|
||||
|
||||
|
||||
// init and terminate
|
||||
|
||||
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
# if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
||||
# define OPENSSL_SIPHASH 1
|
||||
# endif
|
||||
# if (OPENSSL_VERSION_NUMBER >= 0x030500000) // 3.5.0
|
||||
# define OPENSSL_PQ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
|
@ -42,7 +45,11 @@ namespace crypto
|
|||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
||||
|
||||
// DSA
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * CreateDSA (BIGNUM * pubKey = nullptr, BIGNUM * privKey = nullptr);
|
||||
#else
|
||||
DSA * CreateDSA ();
|
||||
#endif
|
||||
|
||||
// RSA
|
||||
const BIGNUM * GetRSAE ();
|
||||
|
@ -248,17 +255,23 @@ namespace crypto
|
|||
struct NoiseSymmetricState
|
||||
{
|
||||
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
|
||||
uint64_t m_N;
|
||||
|
||||
void Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub);
|
||||
|
||||
void MixHash (const uint8_t * buf, size_t len);
|
||||
void MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs);
|
||||
void MixKey (const uint8_t * sharedSecret);
|
||||
|
||||
bool Encrypt (const uint8_t * in, uint8_t * out, size_t len); // out length = len + 16
|
||||
bool Decrypt (const uint8_t * in, uint8_t * out, size_t len); // len without 16 bytes tag
|
||||
};
|
||||
|
||||
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router)
|
||||
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2)
|
||||
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2)
|
||||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
|
||||
|
||||
|
||||
// init and terminate
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -181,5 +181,21 @@ namespace crypto
|
|||
k.GetPrivateKey (priv);
|
||||
memcpy (pub, k.GetPublicKey (), 32);
|
||||
}
|
||||
|
||||
LocalEncryptionKey::LocalEncryptionKey (i2p::data::CryptoKeyType t): keyType(t)
|
||||
{
|
||||
pub.resize (GetCryptoPublicKeyLen (keyType));
|
||||
priv.resize (GetCryptoPrivateKeyLen (keyType));
|
||||
}
|
||||
|
||||
void LocalEncryptionKey::GenerateKeys ()
|
||||
{
|
||||
i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv.data (), pub.data ());
|
||||
}
|
||||
|
||||
void LocalEncryptionKey::CreateDecryptor ()
|
||||
{
|
||||
decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv.data ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include "Crypto.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -157,7 +158,50 @@ namespace crypto
|
|||
X25519Keys m_StaticKeys;
|
||||
};
|
||||
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); // including hybrid
|
||||
|
||||
constexpr size_t GetCryptoPrivateKeyLen (i2p::data::CryptoKeyType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256;
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32;
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32;
|
||||
// ML-KEM hybrid
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
return 32;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr size_t GetCryptoPublicKeyLen (i2p::data::CryptoKeyType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256;
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32;
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32;
|
||||
// ML-KEM hybrid
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
return 32;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct LocalEncryptionKey
|
||||
{
|
||||
std::vector<uint8_t> pub, priv;
|
||||
i2p::data::CryptoKeyType keyType;
|
||||
std::shared_ptr<CryptoKeyDecryptor> decryptor;
|
||||
|
||||
LocalEncryptionKey (i2p::data::CryptoKeyType t);
|
||||
void GenerateKeys ();
|
||||
void CreateDecryptor ();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <vector>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
#include "Timestamp.h"
|
||||
|
@ -377,10 +378,12 @@ namespace client
|
|||
{
|
||||
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
|
||||
uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
|
||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
|
||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE,
|
||||
GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr);
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
|
||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
switch (typeID)
|
||||
{
|
||||
|
@ -395,7 +398,7 @@ namespace client
|
|||
m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
|
||||
break;
|
||||
case eI2NPDatabaseStore:
|
||||
HandleDatabaseStoreMessage (payload, len);
|
||||
HandleDatabaseStoreMessage (payload, len, from);
|
||||
break;
|
||||
case eI2NPDatabaseSearchReply:
|
||||
HandleDatabaseSearchReplyMessage (payload, len);
|
||||
|
@ -410,7 +413,8 @@ namespace client
|
|||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
if (len < DATABASE_STORE_HEADER_SIZE)
|
||||
{
|
||||
|
@ -465,7 +469,8 @@ namespace client
|
|||
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
||||
else
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET],
|
||||
buf + offset, len - offset, true, from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
|
@ -494,7 +499,8 @@ namespace client
|
|||
if (request->requestedBlindedKey)
|
||||
{
|
||||
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
||||
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ());
|
||||
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr,
|
||||
from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType ());
|
||||
if (ls2->IsValid () && !ls2->IsExpired ())
|
||||
{
|
||||
leaseSet = ls2;
|
||||
|
@ -994,17 +1000,10 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const
|
||||
{
|
||||
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
|
||||
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * params):
|
||||
LeaseSetDestination (service, isPublic, params),
|
||||
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||
m_Keys (keys), m_PreferredCryptoType (0), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
|
||||
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
|
||||
m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
|
||||
|
@ -1029,7 +1028,15 @@ namespace client
|
|||
{
|
||||
try
|
||||
{
|
||||
encryptionKeyTypes.insert (std::stoi(it1));
|
||||
i2p::data::CryptoKeyType cryptoType = std::stoi(it1);
|
||||
#if !OPENSSL_PQ
|
||||
if (cryptoType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported
|
||||
#endif
|
||||
{
|
||||
if (!m_PreferredCryptoType && cryptoType)
|
||||
m_PreferredCryptoType = cryptoType; // first non-zero in the list
|
||||
encryptionKeyTypes.insert (cryptoType);
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
|
@ -1046,20 +1053,15 @@ namespace client
|
|||
|
||||
for (auto& it: encryptionKeyTypes)
|
||||
{
|
||||
auto encryptionKey = new EncryptionKey (it);
|
||||
auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it);
|
||||
if (IsPublic ())
|
||||
PersistTemporaryKeys (encryptionKey);
|
||||
else
|
||||
encryptionKey->GenerateKeys ();
|
||||
encryptionKey->CreateDecryptor ();
|
||||
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
m_ECIESx25519EncryptionKey.reset (encryptionKey);
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2
|
||||
}
|
||||
else
|
||||
m_StandardEncryptionKey.reset (encryptionKey);
|
||||
if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1
|
||||
m_EncryptionKeys.emplace (it, encryptionKey);
|
||||
}
|
||||
|
||||
if (IsPublic ())
|
||||
|
@ -1409,31 +1411,56 @@ namespace client
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys)
|
||||
void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys)
|
||||
{
|
||||
if (!keys) return;
|
||||
std::string ident = GetIdentHash().ToBase32();
|
||||
std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
|
||||
std::ifstream f(path, std::ifstream::binary);
|
||||
|
||||
if (f) {
|
||||
f.read ((char *)keys->pub, 256);
|
||||
f.read ((char *)keys->priv, 256);
|
||||
return;
|
||||
if (f)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
||||
len = 512;
|
||||
else if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
f.seekg (0, std::ios::end);
|
||||
len = f.tellg();
|
||||
f.seekg (0, std::ios::beg);
|
||||
}
|
||||
|
||||
if (len == 512)
|
||||
{
|
||||
char pub[256], priv[256];
|
||||
f.read (pub, 256);
|
||||
memcpy (keys->pub.data(), pub, keys->pub.size());
|
||||
f.read (priv, 256);
|
||||
memcpy (keys->priv.data (), priv, keys->priv.size ());
|
||||
}
|
||||
else
|
||||
{
|
||||
f.read ((char *)keys->pub.data(), keys->pub.size());
|
||||
f.read ((char *)keys->priv.data(), keys->priv.size());
|
||||
}
|
||||
if (f)
|
||||
return;
|
||||
else
|
||||
LogPrint(eLogWarning, "Destination: Can't read keys from ", path);
|
||||
}
|
||||
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
|
||||
memset (keys->priv, 0, 256);
|
||||
memset (keys->pub, 0, 256);
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type ", keys->keyType, " for address ", ident, ".b32.i2p");
|
||||
memset (keys->priv.data (), 0, keys->priv.size ());
|
||||
memset (keys->pub.data (), 0, keys->pub.size ());
|
||||
keys->GenerateKeys ();
|
||||
// TODO:: persist crypto key type
|
||||
|
||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (f1) {
|
||||
f1.write ((char *)keys->pub, 256);
|
||||
f1.write ((char *)keys->priv, 256);
|
||||
return;
|
||||
if (f1)
|
||||
{
|
||||
f1.write ((char *)keys->pub.data (), keys->pub.size ());
|
||||
f1.write ((char *)keys->priv.data (), keys->priv.size ());
|
||||
}
|
||||
LogPrint(eLogCritical, "Destinations: Can't save keys to ", path);
|
||||
if (!f1)
|
||||
LogPrint(eLogError, "Destination: Can't save keys to ", path);
|
||||
}
|
||||
|
||||
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
|
@ -1441,9 +1468,10 @@ namespace client
|
|||
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
{
|
||||
if (m_StandardEncryptionKey)
|
||||
auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
if (it != m_EncryptionKeys.end ())
|
||||
{
|
||||
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), m_StandardEncryptionKey->pub, tunnels);
|
||||
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), it->second->pub.data (), tunnels);
|
||||
// sign
|
||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
|
||||
}
|
||||
|
@ -1453,12 +1481,27 @@ namespace client
|
|||
else
|
||||
{
|
||||
// standard LS2 (type 3) first
|
||||
i2p::data::LocalLeaseSet2::KeySections keySections;
|
||||
if (m_ECIESx25519EncryptionKey)
|
||||
keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} );
|
||||
if (m_StandardEncryptionKey)
|
||||
keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} );
|
||||
|
||||
if (m_EncryptionKeys.empty ())
|
||||
{
|
||||
LogPrint (eLogError, "Destinations: No encryption keys");
|
||||
return;
|
||||
}
|
||||
|
||||
i2p::data::LocalLeaseSet2::EncryptionKeys keySections;
|
||||
std::shared_ptr<const i2p::crypto::LocalEncryptionKey> preferredSection;
|
||||
if (m_EncryptionKeys.size () == 1)
|
||||
preferredSection = m_EncryptionKeys.begin ()->second; // only key
|
||||
else
|
||||
{
|
||||
for (const auto& it: m_EncryptionKeys)
|
||||
if (it.first == m_PreferredCryptoType)
|
||||
preferredSection = it.second;
|
||||
else
|
||||
keySections.push_back (it.second);
|
||||
}
|
||||
if (preferredSection)
|
||||
keySections.push_front (preferredSection); // make preferred first
|
||||
|
||||
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
||||
{
|
||||
|
@ -1483,11 +1526,22 @@ namespace client
|
|||
|
||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
|
||||
{
|
||||
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
|
||||
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
|
||||
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey;
|
||||
if (!m_EncryptionKeys.empty ())
|
||||
{
|
||||
if (m_EncryptionKeys.rbegin ()->first == preferredCrypto)
|
||||
encryptionKey = m_EncryptionKeys.rbegin ()->second;
|
||||
else
|
||||
{
|
||||
auto it = m_EncryptionKeys.find (preferredCrypto);
|
||||
if (it != m_EncryptionKeys.end ())
|
||||
encryptionKey = it->second;
|
||||
}
|
||||
if (!encryptionKey)
|
||||
encryptionKey = m_EncryptionKeys.rbegin ()->second;
|
||||
}
|
||||
if (encryptionKey)
|
||||
return encryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
else
|
||||
LogPrint (eLogError, "Destinations: Decryptor is not set");
|
||||
return false;
|
||||
|
@ -1495,14 +1549,26 @@ namespace client
|
|||
|
||||
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
|
||||
{
|
||||
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
return m_EncryptionKeys.contains (keyType);
|
||||
#else
|
||||
return m_EncryptionKeys.count (keyType) > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
i2p::data::CryptoKeyType ClientDestination::GetRatchetsHighestCryptoType () const
|
||||
{
|
||||
if (m_EncryptionKeys.empty ()) return 0;
|
||||
auto cryptoType = m_EncryptionKeys.rbegin ()->first;
|
||||
return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0;
|
||||
}
|
||||
|
||||
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
|
||||
{
|
||||
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
|
||||
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
|
||||
auto it = m_EncryptionKeys.find (keyType);
|
||||
if (it != m_EncryptionKeys.end ())
|
||||
return it->second->pub.data ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Crypto.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.hpp"
|
||||
|
@ -163,17 +164,19 @@ namespace client
|
|||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
|
||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
||||
int GetAuthType () const { return m_AuthType; };
|
||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||
virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0;
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void UpdateLeaseSet ();
|
||||
|
@ -182,7 +185,7 @@ namespace client
|
|||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
|
@ -192,7 +195,6 @@ namespace client
|
|||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -229,25 +231,14 @@ namespace client
|
|||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
struct EncryptionKey
|
||||
{
|
||||
uint8_t pub[256], priv[256];
|
||||
i2p::data::CryptoKeyType keyType;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor;
|
||||
|
||||
EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); };
|
||||
void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); };
|
||||
void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); };
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void Start () override;
|
||||
void Stop () override;
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
|
@ -284,24 +275,28 @@ namespace client
|
|||
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
|
||||
|
||||
// implements LocalDestination
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override { return m_Keys.GetPublic (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override;
|
||||
|
||||
protected:
|
||||
|
||||
void CleanupDestination ();
|
||||
// GarlicDestionation
|
||||
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override;
|
||||
// LeaseSetDestination
|
||||
void CleanupDestination () override;
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const override { return m_PreferredCryptoType; }
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len) override;
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
||||
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
||||
}
|
||||
void PersistTemporaryKeys (EncryptionKey * keys);
|
||||
void PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys);
|
||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||
|
||||
template<typename Dest>
|
||||
|
@ -310,9 +305,9 @@ namespace client
|
|||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
|
||||
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
|
||||
|
||||
std::map<i2p::data::CryptoKeyType, std::shared_ptr<i2p::crypto::LocalEncryptionKey> > m_EncryptionKeys; // last is most preferable
|
||||
i2p::data::CryptoKeyType m_PreferredCryptoType;
|
||||
|
||||
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams;
|
||||
bool m_IsStreamingAnswerPings;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Log.h"
|
||||
#include "util.h"
|
||||
#include "Crypto.h"
|
||||
#include "PostQuantum.h"
|
||||
#include "Elligator.h"
|
||||
#include "Tag.h"
|
||||
#include "I2PEndian.h"
|
||||
|
@ -94,6 +95,17 @@ namespace garlic
|
|||
m_ItermediateSymmKeys.erase (index);
|
||||
}
|
||||
|
||||
ReceiveRatchetTagSet::ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS):
|
||||
m_Session (session), m_IsNS (isNS)
|
||||
{
|
||||
}
|
||||
|
||||
ReceiveRatchetTagSet::~ReceiveRatchetTagSet ()
|
||||
{
|
||||
if (m_IsNS && m_Session)
|
||||
m_Session->CleanupReceiveNSRKeys ();
|
||||
}
|
||||
|
||||
void ReceiveRatchetTagSet::Expire ()
|
||||
{
|
||||
if (!m_ExpirationTimestamp)
|
||||
|
@ -162,12 +174,12 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
if (m_Destination)
|
||||
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
|
||||
GarlicRoutingSession (owner, true)
|
||||
GarlicRoutingSession (owner, true), m_RemoteStaticKeyType (0)
|
||||
{
|
||||
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
||||
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
|
||||
|
@ -251,34 +263,82 @@ namespace garlic
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::CleanupReceiveNSRKeys ()
|
||||
{
|
||||
m_EphemeralKeys = nullptr;
|
||||
#if OPENSSL_PQ
|
||||
m_PQKeys = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (!GetOwner ()) return false;
|
||||
// we are Bob
|
||||
// KDF1
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
||||
|
||||
|
||||
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't decode elligator");
|
||||
return false;
|
||||
}
|
||||
buf += 32; len -= 32;
|
||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
uint8_t sharedSecret[32];
|
||||
if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
||||
bool decrypted = false;
|
||||
auto cryptoType = GetOwner ()->GetRatchetsHighestCryptoType ();
|
||||
#if OPENSSL_PQ
|
||||
if (cryptoType > i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // we support post quantum
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), cryptoType, GetOwner ()->GetEncryptionPublicKey (cryptoType)); // bpk
|
||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
if (GetOwner ()->Decrypt (m_Aepk, sharedSecret, cryptoType)) // x25519(bsk, aepk)
|
||||
{
|
||||
MixKey (sharedSecret);
|
||||
|
||||
auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (cryptoType);
|
||||
std::vector<uint8_t> encapsKey(keyLen);
|
||||
if (Decrypt (buf, encapsKey.data (), keyLen))
|
||||
{
|
||||
decrypted = true; // encaps section has right hash
|
||||
MixHash (buf, keyLen + 16);
|
||||
buf += keyLen + 16;
|
||||
len -= keyLen + 16;
|
||||
|
||||
m_PQKeys = i2p::crypto::CreateMLKEMKeys (cryptoType);
|
||||
m_PQKeys->SetPublicKey (encapsKey.data ());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!decrypted)
|
||||
{
|
||||
if (cryptoType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
||||
GetOwner ()->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
cryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: No supported encryption type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt flags/static
|
||||
uint8_t nonce[12], fs[32];
|
||||
CreateNonce (0, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
|
||||
uint8_t fs[32];
|
||||
if (!Decrypt (buf, fs, 32))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
|
||||
return false;
|
||||
|
@ -290,21 +350,19 @@ namespace garlic
|
|||
bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
|
||||
if (isStatic)
|
||||
{
|
||||
// static key, fs is apk
|
||||
memcpy (m_RemoteStaticKey, fs, 32);
|
||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk)
|
||||
// static key, fs is apk
|
||||
SetRemoteStaticKey (cryptoType, fs);
|
||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, apk)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
else // all zeros flags
|
||||
CreateNonce (1, nonce);
|
||||
|
||||
// decrypt payload
|
||||
std::vector<uint8_t> payload (len - 16); // we must save original ciphertext
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
|
||||
if (!Decrypt (buf, payload.data (), len - 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
|
||||
return false;
|
||||
|
@ -340,7 +398,7 @@ namespace garlic
|
|||
{
|
||||
case eECIESx25519BlkGalicClove:
|
||||
if (GetOwner ())
|
||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size, this);
|
||||
break;
|
||||
case eECIESx25519BlkNextKey:
|
||||
LogPrint (eLogDebug, "Garlic: Next key");
|
||||
|
@ -492,7 +550,16 @@ namespace garlic
|
|||
offset += 32;
|
||||
|
||||
// KDF1
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
{
|
||||
i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), m_RemoteStaticKeyType, m_RemoteStaticKey); // bpk
|
||||
m_PQKeys = i2p::crypto::CreateMLKEMKeys (m_RemoteStaticKeyType);
|
||||
m_PQKeys->GenerateKeys ();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||
uint8_t sharedSecret[32];
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
|
||||
|
@ -501,18 +568,32 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
{
|
||||
auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType);
|
||||
std::vector<uint8_t> encapsKey(keyLen);
|
||||
m_PQKeys->GetPublicKey (encapsKey.data ());
|
||||
// encrypt encapsKey
|
||||
if (!Encrypt (encapsKey.data (), out + offset, keyLen))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: ML-KEM encap_key section AEAD encryption failed ");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + offset, keyLen + 16); // h = SHA256(h || ciphertext)
|
||||
offset += keyLen + 16;
|
||||
}
|
||||
#endif
|
||||
// encrypt flags/static key section
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
const uint8_t * fs;
|
||||
if (isStatic)
|
||||
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||
fs = GetOwner ()->GetEncryptionPublicKey (m_RemoteStaticKeyType);
|
||||
else
|
||||
{
|
||||
memset (out + offset, 0, 32); // all zeros flags section
|
||||
fs = out + offset;
|
||||
}
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
|
||||
if (!Encrypt (fs, out + offset, 32))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
|
||||
return false;
|
||||
|
@ -523,13 +604,11 @@ namespace garlic
|
|||
// KDF2
|
||||
if (isStatic)
|
||||
{
|
||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk)
|
||||
MixKey (sharedSecret);
|
||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bpk)
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
else
|
||||
CreateNonce (1, nonce);
|
||||
// encrypt payload
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
|
||||
if (!Encrypt (payload, out + offset, len))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||
return false;
|
||||
|
@ -567,7 +646,7 @@ namespace garlic
|
|||
}
|
||||
memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR
|
||||
memcpy (m_NSRH, m_H, 32);
|
||||
offset += 32;
|
||||
offset += 32;
|
||||
// KDF for Reply Key Section
|
||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||
|
@ -578,16 +657,33 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
#if OPENSSL_PQ
|
||||
if (m_PQKeys)
|
||||
{
|
||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
||||
std::vector<uint8_t> kemCiphertext(cipherTextLen);
|
||||
m_PQKeys->Encaps (kemCiphertext.data (), sharedSecret);
|
||||
|
||||
if (!Encrypt (kemCiphertext.data (), out + offset, cipherTextLen))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: NSR ML-KEM ciphertext section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
m_NSREncodedPQKey = std::make_unique<std::vector<uint8_t> > (cipherTextLen + 16);
|
||||
memcpy (m_NSREncodedPQKey->data (), out + offset, cipherTextLen + 16);
|
||||
MixHash (out + offset, cipherTextLen + 16);
|
||||
MixKey (sharedSecret);
|
||||
offset += cipherTextLen + 16;
|
||||
}
|
||||
#endif
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// calculate hash for zero length
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||
if (!Encrypt (sharedSecret /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||
return false;
|
||||
|
@ -608,6 +704,7 @@ namespace garlic
|
|||
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||
// encrypt payload
|
||||
uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
|
||||
|
@ -629,16 +726,34 @@ namespace garlic
|
|||
memcpy (m_H, m_NSRH, 32);
|
||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||
m_N = 0;
|
||||
size_t offset = 40;
|
||||
#if OPENSSL_PQ
|
||||
if (m_PQKeys)
|
||||
{
|
||||
if (m_NSREncodedPQKey)
|
||||
{
|
||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
||||
memcpy (out + offset, m_NSREncodedPQKey->data (), cipherTextLen + 16);
|
||||
MixHash (out + offset, cipherTextLen + 16);
|
||||
offset += cipherTextLen + 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: No stored ML-KEM keys");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!Encrypt (m_NSRH /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + 40, 16); // h = SHA256(h || ciphertext)
|
||||
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
|
||||
// encrypt payload
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt
|
||||
uint8_t nonce[12]; memset (nonce, 0, 12);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset + 16, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
|
||||
return false;
|
||||
|
@ -670,13 +785,30 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
{
|
||||
// decrypt kem_ciphertext section
|
||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
||||
std::vector<uint8_t> kemCiphertext(cipherTextLen);
|
||||
if (!Decrypt (buf, kemCiphertext.data (), cipherTextLen))
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply ML-KEM ciphertext section AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (buf, cipherTextLen + 16);
|
||||
buf += cipherTextLen + 16;
|
||||
len -= cipherTextLen + 16;
|
||||
// decaps
|
||||
m_PQKeys->Decaps (kemCiphertext.data (), sharedSecret);
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
#endif
|
||||
GetOwner ()->Decrypt (bepk, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bepk)
|
||||
MixKey (sharedSecret);
|
||||
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
|
||||
// calculate hash for zero length
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
|
||||
if (!Decrypt (buf, sharedSecret/* can be anything */, 0)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
|
||||
return false;
|
||||
|
@ -701,6 +833,7 @@ namespace garlic
|
|||
}
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||
// decrypt payload
|
||||
uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||
|
@ -710,7 +843,8 @@ namespace garlic
|
|||
if (m_State == eSessionStateNewSessionSent)
|
||||
{
|
||||
m_State = eSessionStateEstablished;
|
||||
//m_EphemeralKeys = nullptr; // TODO: delete after a while
|
||||
// don't delete m_EpehemralKey and m_PQKeys because delayd NSR's migth come
|
||||
// done in CleanupReceiveNSRKeys called from NSR tagset destructor
|
||||
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||
}
|
||||
|
@ -803,6 +937,10 @@ namespace garlic
|
|||
m_State = eSessionStateEstablished;
|
||||
m_NSRSendTagset = nullptr;
|
||||
m_EphemeralKeys = nullptr;
|
||||
#if OPENSSL_PQ
|
||||
m_PQKeys = nullptr;
|
||||
m_NSREncodedPQKey = nullptr;
|
||||
#endif
|
||||
[[fallthrough]];
|
||||
case eSessionStateEstablished:
|
||||
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
|
||||
|
@ -833,7 +971,12 @@ namespace garlic
|
|||
if (!payload) return nullptr;
|
||||
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
|
||||
if (!len) return nullptr;
|
||||
#if OPENSSL_PQ
|
||||
auto m = NewI2NPMessage (len + (m_State == eSessionStateEstablished ? 28 :
|
||||
i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 116));
|
||||
#else
|
||||
auto m = NewI2NPMessage (len + 100); // 96 + 4
|
||||
#endif
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
|
||||
|
@ -848,16 +991,28 @@ namespace garlic
|
|||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 96;
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
len += i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 16;
|
||||
#endif
|
||||
break;
|
||||
case eSessionStateNewSessionReceived:
|
||||
if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
|
||||
#endif
|
||||
break;
|
||||
case eSessionStateNewSessionReplySent:
|
||||
if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
#if OPENSSL_PQ
|
||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
||||
len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
|
||||
#endif
|
||||
break;
|
||||
case eSessionStateOneTime:
|
||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "PostQuantum.h"
|
||||
#include "Garlic.h"
|
||||
#include "Tag.h"
|
||||
|
||||
|
@ -79,8 +81,8 @@ namespace garlic
|
|||
{
|
||||
public:
|
||||
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
|
||||
m_Session (session), m_IsNS (isNS) {};
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false);
|
||||
~ReceiveRatchetTagSet () override;
|
||||
|
||||
bool IsNS () const { return m_IsNS; };
|
||||
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
||||
|
@ -168,18 +170,22 @@ namespace garlic
|
|||
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); }
|
||||
|
||||
i2p::data::CryptoKeyType GetRemoteStaticKeyType () const { return m_RemoteStaticKeyType; }
|
||||
void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
|
||||
{
|
||||
m_RemoteStaticKeyType = keyType;
|
||||
memcpy (m_RemoteStaticKey, key, 32);
|
||||
}
|
||||
void Terminate () { m_IsTerminated = true; }
|
||||
void SetDestination (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
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); }
|
||||
|
||||
void CleanupReceiveNSRKeys (); // called from ReceiveRatchetTagSet at Alice's side
|
||||
|
||||
bool IsRatchets () const override { return true; };
|
||||
bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; };
|
||||
bool IsTerminated () const override { return m_IsTerminated; }
|
||||
|
@ -219,10 +225,15 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
i2p::data::CryptoKeyType m_RemoteStaticKeyType;
|
||||
uint8_t m_RemoteStaticKey[32];
|
||||
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
||||
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;
|
||||
#if OPENSSL_PQ
|
||||
std::unique_ptr<i2p::crypto::MLKEMKeys> m_PQKeys;
|
||||
std::unique_ptr<std::vector<uint8_t> > m_NSREncodedPQKey;
|
||||
#endif
|
||||
SessionState m_State = eSessionStateNew;
|
||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
||||
m_LastSentTimestamp = 0; // in milliseconds
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -15,6 +15,10 @@
|
|||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
#include <FindDirectory.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
|
@ -169,12 +173,11 @@ namespace fs {
|
|||
dataDir += "/Library/Application Support/" + appName;
|
||||
return;
|
||||
#elif defined(__HAIKU__)
|
||||
char *home = getenv("HOME");
|
||||
if (home != NULL && strlen(home) > 0) {
|
||||
dataDir = std::string(home) + "/config/settings/" + appName;
|
||||
} else {
|
||||
char home[PATH_MAX]; // /boot/home/config/settings
|
||||
if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, home, PATH_MAX) == B_OK)
|
||||
dataDir = std::string(home) + "/" + appName;
|
||||
else
|
||||
dataDir = "/tmp/" + appName;
|
||||
}
|
||||
return;
|
||||
#else /* other unix */
|
||||
#if defined(ANDROID)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -90,10 +90,10 @@ namespace data
|
|||
}
|
||||
|
||||
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||
const char * signature, const char * key) const
|
||||
std::string_view signature, const char * key) const
|
||||
{
|
||||
uint8_t buf[100], signatureBuf[64];
|
||||
size_t len = family.length (), signatureLen = strlen (signature);
|
||||
size_t len = family.length ();
|
||||
if (len + 32 > 100)
|
||||
{
|
||||
LogPrint (eLogError, "Family: ", family, " is too long");
|
||||
|
@ -105,7 +105,7 @@ namespace data
|
|||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64);
|
||||
if (signatureBufLen)
|
||||
{
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
|
@ -154,12 +154,7 @@ namespace data
|
|||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
signer.Sign (buf, len, signature);
|
||||
len = Base64EncodingBufferSize (64);
|
||||
char * b64 = new char[len+1];
|
||||
len = ByteStreamToBase64 (signature, 64, b64, len);
|
||||
b64[len] = 0;
|
||||
sig = b64;
|
||||
delete[] b64;
|
||||
sig = ByteStreamToBase64 (signature, 64);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include "Identity.h"
|
||||
|
@ -28,7 +29,7 @@ namespace data
|
|||
~Families ();
|
||||
void LoadCertificates ();
|
||||
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||
const char * signature, const char * key = nullptr) const;
|
||||
std::string_view signature, const char * key = nullptr) const;
|
||||
FamilyID GetFamilyID (const std::string& family) const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -498,7 +498,8 @@ namespace garlic
|
|||
buf += 4; // length
|
||||
|
||||
bool found = false;
|
||||
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
bool supportsRatchets = SupportsRatchets ();
|
||||
if (supportsRatchets)
|
||||
// try ECIESx25519 tag
|
||||
found = HandleECIESx25519TagMessage (buf, length);
|
||||
if (!found)
|
||||
|
@ -535,7 +536,7 @@ namespace garlic
|
|||
decryption->Decrypt(buf + 514, length - 514, iv, buf + 514);
|
||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
||||
}
|
||||
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
else if (supportsRatchets)
|
||||
{
|
||||
// otherwise ECIESx25519
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
|
@ -747,31 +748,35 @@ namespace garlic
|
|||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
|
||||
bool requestNewIfNotFound)
|
||||
{
|
||||
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
|
||||
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint8_t staticKey[32];
|
||||
destination->Encrypt (nullptr, staticKey); // we are supposed to get static key
|
||||
auto it = m_ECIESx25519Sessions.find (staticKey);
|
||||
if (it != m_ECIESx25519Sessions.end ())
|
||||
{
|
||||
session = it->second;
|
||||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||
if (SupportsEncryptionType (destination->GetEncryptionType ()))
|
||||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint8_t staticKey[32];
|
||||
destination->Encrypt (nullptr, staticKey); // we are supposed to get static key
|
||||
auto it = m_ECIESx25519Sessions.find (staticKey);
|
||||
if (it != m_ECIESx25519Sessions.end ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||
requestNewIfNotFound = true; // it's not a new session
|
||||
session = nullptr;
|
||||
session = it->second;
|
||||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||
requestNewIfNotFound = true; // it's not a new session
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
if (!session && requestNewIfNotFound)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||
session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey);
|
||||
}
|
||||
if (session && destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
||||
return session;
|
||||
}
|
||||
if (!session && requestNewIfNotFound)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||
session->SetRemoteStaticKey (staticKey);
|
||||
}
|
||||
if (session && destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
||||
return session;
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -999,7 +1004,8 @@ namespace garlic
|
|||
i2p::fs::Remove (it);
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
|
||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len,
|
||||
ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
const uint8_t * buf1 = buf;
|
||||
uint8_t flag = buf[0]; buf++; // flag
|
||||
|
@ -1019,7 +1025,7 @@ namespace garlic
|
|||
buf += 4; // expiration
|
||||
ptrdiff_t offset = buf - buf1;
|
||||
if (offset <= (int)len)
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID, from);
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Clove is too long");
|
||||
break;
|
||||
|
|
|
@ -257,7 +257,7 @@ namespace garlic
|
|||
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||
void RemoveECIESx25519Session (const uint8_t * staticKey);
|
||||
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
|
||||
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, ECIESX25519AEADRatchetSession * from);
|
||||
uint8_t * GetPayloadBuffer ();
|
||||
|
||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
@ -266,21 +266,27 @@ namespace garlic
|
|||
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||
virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const
|
||||
{
|
||||
return GetIdentity ()->GetCryptoKeyType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? GetIdentity ()->GetCryptoKeyType () : 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0;
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
void SaveTags ();
|
||||
void LoadTags ();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; }
|
||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -10,6 +10,7 @@
|
|||
#include <utility>
|
||||
#include <stdio.h>
|
||||
#include <ctime>
|
||||
#include <charconv>
|
||||
#include "util.h"
|
||||
#include "Base.h"
|
||||
#include "HTTP.h"
|
||||
|
@ -18,54 +19,51 @@ namespace i2p
|
|||
{
|
||||
namespace http
|
||||
{
|
||||
const std::vector<std::string> HTTP_METHODS = {
|
||||
// list of valid HTTP methods
|
||||
static constexpr std::array<std::string_view, 16> HTTP_METHODS =
|
||||
{
|
||||
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
|
||||
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
|
||||
};
|
||||
const std::vector<std::string> HTTP_VERSIONS = {
|
||||
|
||||
// list of valid HTTP versions
|
||||
static constexpr std::array<std::string_view, 2> HTTP_VERSIONS =
|
||||
{
|
||||
"HTTP/1.0", "HTTP/1.1"
|
||||
};
|
||||
const std::vector<const char *> weekdays = {
|
||||
|
||||
static constexpr std::array<const char *, 7> weekdays =
|
||||
{
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
const std::vector<const char *> months = {
|
||||
|
||||
static constexpr std::array<const char *, 12> months =
|
||||
{
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
inline bool is_http_version(const std::string & str) {
|
||||
static inline bool is_http_version(std::string_view str)
|
||||
{
|
||||
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
|
||||
}
|
||||
|
||||
inline bool is_http_method(const std::string & str) {
|
||||
static inline bool is_http_method(std::string_view str)
|
||||
{
|
||||
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
|
||||
}
|
||||
|
||||
static void strsplit(std::stringstream& ss, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
std::string token;
|
||||
while (1)
|
||||
static void strsplit(std::string_view line, std::vector<std::string_view> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
size_t count = 0, pos;
|
||||
while ((pos = line.find (delim)) != line.npos)
|
||||
{
|
||||
count++;
|
||||
if (limit > 0 && count >= limit)
|
||||
delim = '\n'; /* reset delimiter */
|
||||
if (!std::getline(ss, token, delim))
|
||||
break;
|
||||
tokens.push_back(token);
|
||||
if (limit > 0 && count >= limit) delim = '\n'; // reset delimiter
|
||||
tokens.push_back (line.substr (0, pos));
|
||||
line = line.substr (pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
std::stringstream ss{line};
|
||||
strsplit (ss, tokens, delim, limit);
|
||||
}
|
||||
|
||||
static void strsplit(std::string_view line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
std::stringstream ss{std::string(line)};
|
||||
strsplit (ss, tokens, delim, limit);
|
||||
if (!line.empty ()) tokens.push_back (line);
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
|
||||
|
@ -211,8 +209,9 @@ namespace http
|
|||
return true;
|
||||
}
|
||||
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params) {
|
||||
std::vector<std::string> tokens;
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params)
|
||||
{
|
||||
std::vector<std::string_view> tokens;
|
||||
strsplit(query, tokens, '&');
|
||||
|
||||
params.clear();
|
||||
|
@ -308,8 +307,9 @@ namespace http
|
|||
if (expect == REQ_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<std::string_view> tokens;
|
||||
strsplit(line, tokens, ' ');
|
||||
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_method(tokens[0]))
|
||||
|
@ -333,11 +333,11 @@ namespace http
|
|||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
pos = eol + CRLF.length();
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
return eoh + HTTP_EOH.length();
|
||||
}
|
||||
|
||||
void HTTPReq::write(std::ostream & o)
|
||||
|
@ -381,7 +381,7 @@ namespace http
|
|||
}
|
||||
}
|
||||
|
||||
std::string HTTPReq::GetHeader (const std::string& name) const
|
||||
std::string HTTPReq::GetHeader (std::string_view name) const
|
||||
{
|
||||
for (auto& it : headers)
|
||||
if (it.first == name)
|
||||
|
@ -389,7 +389,7 @@ namespace http
|
|||
return "";
|
||||
}
|
||||
|
||||
size_t HTTPReq::GetNumHeaders (const std::string& name) const
|
||||
size_t HTTPReq::GetNumHeaders (std::string_view name) const
|
||||
{
|
||||
size_t num = 0;
|
||||
for (auto& it : headers)
|
||||
|
@ -451,13 +451,15 @@ namespace http
|
|||
if (expect == RES_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<std::string_view> tokens;
|
||||
strsplit(line, tokens, ' ', 3);
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_version(tokens[0]))
|
||||
return -1;
|
||||
code = atoi(tokens[1].c_str());
|
||||
auto res = std::from_chars(tokens[1].data (), tokens[1].data() + tokens[1].size(), code);
|
||||
if (res.ec != std::errc())
|
||||
return -1;
|
||||
if (code < 100 || code >= 600)
|
||||
return -1;
|
||||
/* all ok */
|
||||
|
@ -474,11 +476,11 @@ namespace http
|
|||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
pos = eol + CRLF.length();
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
return eoh + HTTP_EOH.length();
|
||||
}
|
||||
|
||||
std::string HTTPRes::to_string() {
|
||||
|
@ -503,9 +505,11 @@ namespace http
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
const char * HTTPCodeToStatus(int code) {
|
||||
const char *ptr;
|
||||
switch (code) {
|
||||
std::string_view HTTPCodeToStatus(int code)
|
||||
{
|
||||
std::string_view ptr;
|
||||
switch (code)
|
||||
{
|
||||
case 105: ptr = "Name Not Resolved"; break;
|
||||
/* success */
|
||||
case 200: ptr = "OK"; break;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -21,10 +21,8 @@ namespace i2p
|
|||
{
|
||||
namespace http
|
||||
{
|
||||
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
|
||||
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
||||
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
|
||||
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
|
||||
constexpr std::string_view CRLF = "\r\n"; /**< HTTP line terminator */
|
||||
constexpr std::string_view HTTP_EOH = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
||||
|
||||
struct URL
|
||||
{
|
||||
|
@ -103,8 +101,8 @@ namespace http
|
|||
void UpdateHeader (const std::string& name, const std::string& value);
|
||||
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
|
||||
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
|
||||
std::string GetHeader (const std::string& name) const;
|
||||
size_t GetNumHeaders (const std::string& name) const;
|
||||
std::string GetHeader (std::string_view name) const;
|
||||
size_t GetNumHeaders (std::string_view name) const;
|
||||
size_t GetNumHeaders () const { return headers.size (); };
|
||||
};
|
||||
|
||||
|
@ -154,7 +152,7 @@ namespace http
|
|||
* @param code HTTP code [100, 599]
|
||||
* @return Immutable string with status
|
||||
*/
|
||||
const char * HTTPCodeToStatus(int code);
|
||||
std::string_view HTTPCodeToStatus(int code);
|
||||
|
||||
/**
|
||||
* @brief Replaces %-encoded characters in string with their values
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -119,6 +120,16 @@ namespace data
|
|||
memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
{
|
||||
memcpy (m_StandardIdentity, signingKey, 384);
|
||||
excessLen = i2p::crypto::MLDSA44_PUBLIC_KEY_LENGTH - 384;
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 384, excessLen);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
||||
}
|
||||
|
@ -261,21 +272,17 @@ namespace data
|
|||
|
||||
size_t IdentityEx::FromBase64(std::string_view s)
|
||||
{
|
||||
const size_t slen = s.length();
|
||||
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
|
||||
const size_t len = Base64ToByteStream (s.data(), slen, buf.data(), slen);
|
||||
std::vector<uint8_t> buf(s.length ()); // binary data can't exceed base64
|
||||
auto len = Base64ToByteStream (s, buf.data(), buf.size ());
|
||||
return FromBuffer (buf.data(), len);
|
||||
}
|
||||
|
||||
std::string IdentityEx::ToBase64 () const
|
||||
{
|
||||
const size_t bufLen = GetFullLen();
|
||||
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
||||
std::vector<uint8_t> buf(bufLen);
|
||||
std::vector<char> str(strLen);
|
||||
size_t l = ToBuffer (buf.data(), bufLen);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen);
|
||||
return std::string (str.data(), l1);
|
||||
return i2p::data::ByteStreamToBase64 (buf.data(), l);
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
||||
|
@ -352,6 +359,10 @@ namespace data
|
|||
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
|
||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
return new i2p::crypto::RedDSA25519Verifier ();
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
return new i2p::crypto::MLDSA44Verifier ();
|
||||
#endif
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
|
@ -373,6 +384,18 @@ namespace data
|
|||
auto keyLen = verifier->GetPublicKeyLen ();
|
||||
if (keyLen <= 128)
|
||||
verifier->SetPublicKey (m_StandardIdentity.signingKey + 128 - keyLen);
|
||||
#if OPENSSL_PQ
|
||||
else if (keyLen > 384)
|
||||
{
|
||||
// for post-quantum
|
||||
uint8_t * signingKey = new uint8_t[keyLen];
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 384);
|
||||
size_t excessLen = keyLen - 384;
|
||||
memcpy (signingKey + 384, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
verifier->SetPublicKey (signingKey);
|
||||
delete[] signingKey;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// for P521
|
||||
|
@ -396,15 +419,14 @@ namespace data
|
|||
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
return std::make_shared<i2p::crypto::ECIESGOSTR3410Encryptor>(key);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType);
|
||||
};
|
||||
|
@ -548,26 +570,18 @@ namespace data
|
|||
return ret;
|
||||
}
|
||||
|
||||
size_t PrivateKeys::FromBase64(const std::string& s)
|
||||
size_t PrivateKeys::FromBase64(std::string_view s)
|
||||
{
|
||||
uint8_t * buf = new uint8_t[s.length ()];
|
||||
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
||||
size_t ret = FromBuffer (buf, l);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
std::vector<uint8_t> buf(s.length ());
|
||||
size_t l = i2p::data::Base64ToByteStream (s, buf.data (), buf.size ());
|
||||
return FromBuffer (buf.data (), l);
|
||||
}
|
||||
|
||||
std::string PrivateKeys::ToBase64 () const
|
||||
{
|
||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||
char * str = new char[GetFullLen ()*2];
|
||||
size_t l = ToBuffer (buf, GetFullLen ());
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
|
||||
str[l1] = 0;
|
||||
delete[] buf;
|
||||
std::string ret(str);
|
||||
delete[] str;
|
||||
return ret;
|
||||
std::vector<uint8_t> buf(GetFullLen ());
|
||||
size_t l = ToBuffer (buf.data (), buf.size ());
|
||||
return i2p::data::ByteStreamToBase64 (buf.data (), l);
|
||||
}
|
||||
|
||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
|
@ -630,6 +644,11 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
return new i2p::crypto::RedDSA25519Signer (priv);
|
||||
break;
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
return new i2p::crypto::MLDSA44Signer (priv);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
|
@ -643,8 +662,7 @@ namespace data
|
|||
|
||||
size_t PrivateKeys::GetPrivateKeyLen () const
|
||||
{
|
||||
// private key length always 256, but type 4
|
||||
return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256;
|
||||
return i2p::crypto::GetCryptoPrivateKeyLen (m_Public->GetCryptoKeyType ());
|
||||
}
|
||||
|
||||
uint8_t * PrivateKeys::GetPadding()
|
||||
|
@ -670,15 +688,14 @@ namespace data
|
|||
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
|
||||
};
|
||||
|
@ -739,6 +756,11 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
|
||||
break;
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
i2p::crypto::CreateMLDSA44RandomKeys (priv, pub);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1
|
||||
|
@ -753,13 +775,12 @@ namespace data
|
|||
i2p::crypto::GenerateElGamalKeyPair(priv, pub);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||
i2p::crypto::CreateECIESP256RandomKeys (priv, pub);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -17,10 +17,14 @@
|
|||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "Signature.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
class CryptoKeyEncryptor;
|
||||
class CryptoKeyDecryptor;
|
||||
}
|
||||
namespace data
|
||||
{
|
||||
typedef Tag<32> IdentHash;
|
||||
|
@ -55,6 +59,8 @@ namespace data
|
|||
Identity& operator=(const Keys& keys);
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
IdentHash Hash () const;
|
||||
operator uint8_t * () { return reinterpret_cast<uint8_t *>(this); }
|
||||
operator const uint8_t * () const { return reinterpret_cast<const uint8_t *>(this); }
|
||||
};
|
||||
|
||||
Keys CreateRandomKeys ();
|
||||
|
@ -64,9 +70,10 @@ namespace data
|
|||
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
|
||||
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD = 5;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD = 6;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD = 7;
|
||||
|
||||
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
|
||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
|
||||
|
@ -75,11 +82,12 @@ namespace data
|
|||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented
|
||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // since openssl 3.0.0
|
||||
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
|
||||
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
|
||||
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
|
||||
|
||||
const uint16_t SIGNING_KEY_TYPE_MLDSA44 = 12;
|
||||
|
||||
typedef uint16_t SigningKeyType;
|
||||
typedef uint16_t CryptoKeyType;
|
||||
|
||||
|
@ -134,7 +142,7 @@ namespace data
|
|||
IdentHash m_IdentHash;
|
||||
std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
|
||||
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; // TODO: support PQ keys
|
||||
};
|
||||
|
||||
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
|
||||
|
@ -163,7 +171,7 @@ namespace data
|
|||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||
|
||||
size_t FromBase64(const std::string& s);
|
||||
size_t FromBase64(std::string_view s);
|
||||
std::string ToBase64 () const;
|
||||
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -14,6 +14,7 @@
|
|||
#include "Timestamp.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Tunnel.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -399,6 +400,7 @@ namespace data
|
|||
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||
// key sections
|
||||
CryptoKeyType preferredKeyType = m_EncryptionType;
|
||||
m_EncryptionType = 0;
|
||||
bool preferredKeyFound = false;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numKeySections = buf[offset]; offset++;
|
||||
|
@ -410,14 +412,22 @@ namespace data
|
|||
if (offset + encryptionKeyLen > len) return 0;
|
||||
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
||||
{
|
||||
// we pick first valid key if preferred not found
|
||||
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
||||
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
|
||||
{
|
||||
m_Encryptor = encryptor; // TODO: atomic
|
||||
m_EncryptionType = keyType;
|
||||
if (keyType == preferredKeyType) preferredKeyFound = true;
|
||||
}
|
||||
// we pick max key type if preferred not found
|
||||
#if !OPENSSL_PQ
|
||||
if (keyType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported
|
||||
#endif
|
||||
{
|
||||
if (keyType == preferredKeyType || !m_Encryptor || keyType > m_EncryptionType)
|
||||
{
|
||||
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
||||
if (encryptor)
|
||||
{
|
||||
m_Encryptor = encryptor; // TODO: atomic
|
||||
m_EncryptionType = keyType;
|
||||
if (keyType == preferredKeyType) preferredKeyFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += encryptionKeyLen;
|
||||
}
|
||||
|
@ -838,7 +848,7 @@ namespace data
|
|||
}
|
||||
|
||||
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
const EncryptionKeys& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
|
||||
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
||||
{
|
||||
|
@ -848,7 +858,7 @@ namespace data
|
|||
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
||||
size_t keySectionsLen = 0;
|
||||
for (const auto& it: encryptionKeys)
|
||||
keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/;
|
||||
keySectionsLen += 2/*key type*/ + 2/*key len*/ + it->pub.size()/*key*/;
|
||||
m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ +
|
||||
1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
|
||||
uint16_t flags = 0;
|
||||
|
@ -883,9 +893,9 @@ namespace data
|
|||
m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key
|
||||
for (const auto& it: encryptionKeys)
|
||||
{
|
||||
htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type
|
||||
htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len
|
||||
memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key
|
||||
htobe16buf (m_Buffer + offset, it->keyType); offset += 2; // key type
|
||||
htobe16buf (m_Buffer + offset, it->pub.size()); offset += 2; // key len
|
||||
memcpy (m_Buffer + offset, it->pub.data(), it->pub.size()); offset += it->pub.size(); // key
|
||||
}
|
||||
// leases
|
||||
uint32_t expirationTime = 0; // in seconds
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -12,12 +12,14 @@
|
|||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Blinding.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -150,8 +152,8 @@ namespace data
|
|||
public:
|
||||
|
||||
LeaseSet2 (uint8_t storeType): LeaseSet (true), m_StoreType (storeType) {}; // for update
|
||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
|
||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // store type 5, called from local netdb only
|
||||
uint8_t GetStoreType () const { return m_StoreType; };
|
||||
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
|
@ -247,15 +249,10 @@ namespace data
|
|||
{
|
||||
public:
|
||||
|
||||
struct KeySection
|
||||
{
|
||||
uint16_t keyType, keyLen;
|
||||
const uint8_t * encryptionPublicKey;
|
||||
};
|
||||
typedef std::vector<KeySection> KeySections;
|
||||
typedef std::list<std::shared_ptr<const i2p::crypto::LocalEncryptionKey> > EncryptionKeys;
|
||||
|
||||
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
const KeySections& encryptionKeys,
|
||||
const EncryptionKeys& encryptionKeys,
|
||||
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, uint64_t publishedTimestamp,
|
||||
bool isPublishedEncrypted = false);
|
||||
|
|
|
@ -145,10 +145,12 @@ namespace transport
|
|||
// 2 bytes reserved
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
||||
// 4 bytes reserved
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
|
||||
// encrypt options
|
||||
if (!Encrypt (options, m_SessionRequestBuffer + 32, 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest failed to encrypt options");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -167,14 +169,16 @@ namespace transport
|
|||
memset (options, 0, 16);
|
||||
htobe16buf (options + 2, paddingLen); // padLen
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||
// encrypt options
|
||||
if (!Encrypt (options, m_SessionCreatedBuffer + 32, 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated failed to encrypt options");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart1 ()
|
||||
{
|
||||
// update AD
|
||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||
|
@ -182,19 +186,28 @@ namespace transport
|
|||
if (paddingLength > 0)
|
||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||
|
||||
// part1 48 bytes
|
||||
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
|
||||
// part1 48 bytes, n = 1
|
||||
if (!Encrypt (i2p::context.GetNTCP2StaticPublicKey (), m_SessionConfirmedBuffer, 32))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part1");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 ()
|
||||
{
|
||||
// part 2
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
// encrypt m3p2, it must be filled in SessionRequest
|
||||
if (!KDF3Alice ()) return false;
|
||||
if (!KDF3Alice ()) return false; // MixKey, n = 0
|
||||
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
|
||||
if (!Encrypt (m3p2, m3p2, m3p2Len - 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part2");
|
||||
return false;
|
||||
}
|
||||
// update h again
|
||||
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
||||
return true;
|
||||
|
@ -214,10 +227,9 @@ namespace transport
|
|||
LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed");
|
||||
return false;
|
||||
}
|
||||
// verify MAC and decrypt options block (32 bytes), use m_H as AD
|
||||
uint8_t nonce[12], options[16];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt
|
||||
// verify MAC and decrypt options block (32 bytes)
|
||||
uint8_t options[16];
|
||||
if (Decrypt (m_SessionRequestBuffer + 32, options, 16))
|
||||
{
|
||||
// options
|
||||
if (options[0] && options[0] != i2p::context.GetNetID ())
|
||||
|
@ -274,9 +286,7 @@ namespace transport
|
|||
}
|
||||
// decrypt and verify MAC
|
||||
uint8_t payload[16];
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt
|
||||
if (Decrypt (m_SessionCreatedBuffer + 32, payload, 16))
|
||||
{
|
||||
// options
|
||||
paddingLen = bufbe16toh(payload + 2);
|
||||
|
@ -297,7 +307,7 @@ namespace transport
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 ()
|
||||
{
|
||||
// update AD
|
||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||
|
@ -305,7 +315,8 @@ namespace transport
|
|||
if (paddingLength > 0)
|
||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S
|
||||
// decrypt S, n = 1
|
||||
if (!Decrypt (m_SessionConfirmedBuffer, m_RemoteStaticKey, 32))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
|
||||
return false;
|
||||
|
@ -313,17 +324,17 @@ namespace transport
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf)
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf)
|
||||
{
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
|
||||
if (!KDF3Bob ())
|
||||
if (!KDF3Bob ()) // MixKey, n = 0
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed");
|
||||
return false;
|
||||
}
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
if (Decrypt (m_SessionConfirmedBuffer + 48, m3p2Buf, m3p2Len - 16))
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
else
|
||||
|
@ -657,11 +668,12 @@ namespace transport
|
|||
|
||||
void NTCP2Session::SendSessionConfirmed ()
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce); // set nonce to 1
|
||||
m_Establisher->CreateSessionConfirmedMessagePart1 (nonce);
|
||||
memset (nonce, 0, 12); // set nonce back to 0
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart2 (nonce))
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart1 ())
|
||||
{
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart2 ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
|
@ -740,14 +752,11 @@ namespace transport
|
|||
// run on establisher thread
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 ())
|
||||
{
|
||||
// part 2
|
||||
auto buf = std::make_shared<std::vector<uint8_t> > (m_Establisher->m3p2Len - 16); // -MAC
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf->data ())) // TODO:handle in establisher thread
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (buf->data ())) // TODO:handle in establisher thread
|
||||
{
|
||||
// payload
|
||||
// RI block must be first
|
||||
|
|
|
@ -91,7 +91,6 @@ namespace transport
|
|||
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
|
||||
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
|
||||
|
||||
const uint8_t * GetK () const { return m_CK + 32; };
|
||||
const uint8_t * GetCK () const { return m_CK; };
|
||||
const uint8_t * GetH () const { return m_H; };
|
||||
|
||||
|
@ -108,13 +107,13 @@ namespace transport
|
|||
|
||||
bool CreateSessionRequestMessage (std::mt19937& rng);
|
||||
bool CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
bool CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
bool CreateSessionConfirmedMessagePart1 ();
|
||||
bool CreateSessionConfirmedMessagePart2 ();
|
||||
|
||||
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
||||
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
||||
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
|
||||
bool ProcessSessionConfirmedMessagePart1 ();
|
||||
bool ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf);
|
||||
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
|
||||
|
|
|
@ -688,7 +688,7 @@ namespace data
|
|||
// since update was long time ago we assume that router is not connected anymore
|
||||
r->ScheduleBufferToDelete ();
|
||||
|
||||
if (r->GetProfile ()->IsUnreachable ())
|
||||
if (r->HasProfile () && r->GetProfile ()->IsUnreachable ())
|
||||
r->SetUnreachable (true);
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
|
||||
|
@ -715,8 +715,9 @@ namespace data
|
|||
r->SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
// make router reachable back if connected now
|
||||
if (r->IsUnreachable () && i2p::transport::transports.IsConnected (ident))
|
||||
// make router reachable back if connected now or trusted router
|
||||
if (r->IsUnreachable () && (i2p::transport::transports.IsConnected (ident) ||
|
||||
i2p::transport::transports.IsTrustedRouter (ident)))
|
||||
r->SetUnreachable (false);
|
||||
|
||||
if (r->IsUnreachable ())
|
||||
|
@ -950,14 +951,13 @@ namespace data
|
|||
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
|
||||
return;
|
||||
}
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
std::string key;
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
||||
|
||||
IdentHash replyIdent(buf + 32);
|
||||
uint8_t flag = buf[64];
|
||||
|
||||
|
||||
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag);
|
||||
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
|
||||
const uint8_t * excluded = buf + 65;
|
||||
|
|
|
@ -360,11 +360,12 @@ namespace data
|
|||
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
const uint8_t * buf = msg->GetPayload ();
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
std::string key;
|
||||
size_t num = buf[32]; // num
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
||||
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
|
||||
|
||||
IdentHash ident (buf);
|
||||
bool isExploratory = false;
|
||||
auto dest = FindRequest (ident);
|
||||
|
|
160
libi2pd/PostQuantum.cpp
Normal file
160
libi2pd/PostQuantum.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "PostQuantum.h"
|
||||
|
||||
#if OPENSSL_PQ
|
||||
|
||||
#include <openssl/param_build.h>
|
||||
#include <openssl/core_names.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
MLKEMKeys::MLKEMKeys (MLKEMTypes type):
|
||||
m_Name (std::get<0>(MLKEMS[type])), m_KeyLen (std::get<1>(MLKEMS[type])),
|
||||
m_CTLen (std::get<2>(MLKEMS[type])), m_Pkey (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
MLKEMKeys::~MLKEMKeys ()
|
||||
{
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
}
|
||||
|
||||
void MLKEMKeys::GenerateKeys ()
|
||||
{
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
m_Pkey = EVP_PKEY_Q_keygen(NULL, NULL, m_Name.c_str ());
|
||||
}
|
||||
|
||||
void MLKEMKeys::GetPublicKey (uint8_t * pub) const
|
||||
{
|
||||
if (m_Pkey)
|
||||
{
|
||||
size_t len = m_KeyLen;
|
||||
EVP_PKEY_get_octet_string_param (m_Pkey, OSSL_PKEY_PARAM_PUB_KEY, pub, m_KeyLen, &len);
|
||||
}
|
||||
}
|
||||
|
||||
void MLKEMKeys::SetPublicKey (const uint8_t * pub)
|
||||
{
|
||||
if (m_Pkey)
|
||||
{
|
||||
EVP_PKEY_free (m_Pkey);
|
||||
m_Pkey = nullptr;
|
||||
}
|
||||
OSSL_PARAM params[] =
|
||||
{
|
||||
OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)pub, m_KeyLen),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, m_Name.c_str (), NULL);
|
||||
if (ctx)
|
||||
{
|
||||
EVP_PKEY_fromdata_init (ctx);
|
||||
EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
||||
}
|
||||
|
||||
void MLKEMKeys::Encaps (uint8_t * ciphertext, uint8_t * shared)
|
||||
{
|
||||
if (!m_Pkey) return;
|
||||
auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
||||
if (ctx)
|
||||
{
|
||||
EVP_PKEY_encapsulate_init (ctx, NULL);
|
||||
size_t len = m_CTLen, sharedLen = 32;
|
||||
EVP_PKEY_encapsulate (ctx, ciphertext, &len, shared, &sharedLen);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
||||
}
|
||||
|
||||
void MLKEMKeys::Decaps (const uint8_t * ciphertext, uint8_t * shared)
|
||||
{
|
||||
if (!m_Pkey) return;
|
||||
auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
||||
if (ctx)
|
||||
{
|
||||
EVP_PKEY_decapsulate_init (ctx, NULL);
|
||||
size_t sharedLen = 32;
|
||||
EVP_PKEY_decapsulate (ctx, shared, &sharedLen, ciphertext, m_CTLen);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
||||
}
|
||||
|
||||
std::unique_ptr<MLKEMKeys> CreateMLKEMKeys (i2p::data::CryptoKeyType type)
|
||||
{
|
||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return nullptr;
|
||||
return std::make_unique<MLKEMKeys>((MLKEMTypes)(type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1));
|
||||
}
|
||||
|
||||
static constexpr std::array<std::pair<std::array<uint8_t, 32>, std::array<uint8_t, 32> >, 3> NoiseIKInitMLKEMKeys =
|
||||
{
|
||||
std::make_pair
|
||||
(
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0xb0, 0x8f, 0xb1, 0x73, 0x92, 0x66, 0xc9, 0x90, 0x45, 0x7f, 0xdd, 0xc6, 0x4e, 0x55, 0x40, 0xd8,
|
||||
0x0a, 0x37, 0x99, 0x06, 0x92, 0x2a, 0x78, 0xc4, 0xb1, 0xef, 0x86, 0x06, 0xd0, 0x15, 0x9f, 0x4d
|
||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM512_ChaChaPoly_SHA256")
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0x95, 0x8d, 0xf6, 0x6c, 0x95, 0xce, 0xa9, 0xf7, 0x42, 0xfc, 0xfa, 0x62, 0x71, 0x36, 0x1e, 0xa7,
|
||||
0xdc, 0x7a, 0xc0, 0x75, 0x01, 0xcf, 0xf9, 0xfc, 0x9f, 0xdb, 0x4c, 0x68, 0x3a, 0x53, 0x49, 0xeb
|
||||
} // SHA256 (first)
|
||||
),
|
||||
std::make_pair
|
||||
(
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0x36, 0x03, 0x90, 0x2d, 0xf9, 0xa2, 0x2a, 0x5e, 0xc9, 0x3d, 0xdb, 0x8f, 0xa8, 0x1b, 0xdb, 0x4b,
|
||||
0xae, 0x9d, 0x93, 0x9c, 0xdf, 0xaf, 0xde, 0x55, 0x49, 0x13, 0xfe, 0x98, 0xf8, 0x4a, 0xd4, 0xbd
|
||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM768_ChaChaPoly_SHA256")
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0x15, 0x44, 0x89, 0xbf, 0x30, 0xf0, 0xc9, 0x77, 0x66, 0x10, 0xcb, 0xb1, 0x57, 0x3f, 0xab, 0x68,
|
||||
0x79, 0x57, 0x39, 0x57, 0x0a, 0xe7, 0xc0, 0x31, 0x8a, 0xa2, 0x96, 0xef, 0xbf, 0xa9, 0x6a, 0xbb
|
||||
} // SHA256 (first)
|
||||
),
|
||||
std::make_pair
|
||||
(
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0x86, 0xa5, 0x36, 0x44, 0xc6, 0x12, 0xd5, 0x71, 0xa1, 0x2d, 0xd8, 0xb6, 0x0a, 0x00, 0x9f, 0x2c,
|
||||
0x1a, 0xa8, 0x7d, 0x22, 0xa4, 0xff, 0x2b, 0xcd, 0x61, 0x34, 0x97, 0x6d, 0xa1, 0x49, 0xeb, 0x4a
|
||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM1024_ChaChaPoly_SHA256")
|
||||
std::array<uint8_t, 32>
|
||||
{
|
||||
0x42, 0x0d, 0xc2, 0x1c, 0x7b, 0x18, 0x61, 0xb7, 0x4a, 0x04, 0x3d, 0xae, 0x0f, 0xdc, 0xf2, 0x71,
|
||||
0xb9, 0xba, 0x19, 0xbb, 0xbd, 0x5f, 0xd4, 0x9c, 0x3f, 0x4b, 0x01, 0xed, 0x6d, 0x13, 0x1d, 0xa2
|
||||
} // SHA256 (first)
|
||||
)
|
||||
};
|
||||
|
||||
void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub)
|
||||
{
|
||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)NoiseIKInitMLKEMKeys.size ()) return;
|
||||
auto ind = type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1;
|
||||
state.Init (NoiseIKInitMLKEMKeys[ind].first.data(), NoiseIKInitMLKEMKeys[ind].second.data(), pub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
88
libi2pd/PostQuantum.h
Normal file
88
libi2pd/PostQuantum.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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
|
||||
*/
|
||||
|
||||
#ifndef POST_QUANTUM_H__
|
||||
#define POST_QUANTUM_H__
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include "Crypto.h"
|
||||
#include "Identity.h"
|
||||
|
||||
#if OPENSSL_PQ
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
enum MLKEMTypes
|
||||
{
|
||||
eMLKEM512 = 0,
|
||||
eMLKEM768,
|
||||
eMLKEM1024
|
||||
};
|
||||
|
||||
constexpr size_t MLKEM512_KEY_LENGTH = 800;
|
||||
constexpr size_t MLKEM512_CIPHER_TEXT_LENGTH = 768;
|
||||
constexpr size_t MLKEM768_KEY_LENGTH = 1184;
|
||||
constexpr size_t MLKEM768_CIPHER_TEXT_LENGTH = 1088;
|
||||
constexpr size_t MLKEM1024_KEY_LENGTH = 1568;
|
||||
constexpr size_t MLKEM1024_CIPHER_TEXT_LENGTH = 1568;
|
||||
|
||||
constexpr std::array<std::tuple<std::string_view, size_t, size_t>, 3> MLKEMS =
|
||||
{
|
||||
std::make_tuple ("ML-KEM-512", MLKEM512_KEY_LENGTH, MLKEM512_CIPHER_TEXT_LENGTH),
|
||||
std::make_tuple ("ML-KEM-768", MLKEM768_KEY_LENGTH, MLKEM768_CIPHER_TEXT_LENGTH),
|
||||
std::make_tuple ("ML-KEM-1024", MLKEM1024_KEY_LENGTH, MLKEM1024_CIPHER_TEXT_LENGTH)
|
||||
};
|
||||
|
||||
constexpr size_t GetMLKEMPublicKeyLen (i2p::data::CryptoKeyType type)
|
||||
{
|
||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0;
|
||||
return std::get<1>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]);
|
||||
}
|
||||
|
||||
constexpr size_t GetMLKEMCipherTextLen (i2p::data::CryptoKeyType type)
|
||||
{
|
||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0;
|
||||
return std::get<2>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]);
|
||||
}
|
||||
|
||||
class MLKEMKeys
|
||||
{
|
||||
public:
|
||||
|
||||
MLKEMKeys (MLKEMTypes type);
|
||||
~MLKEMKeys ();
|
||||
|
||||
void GenerateKeys ();
|
||||
void GetPublicKey (uint8_t * pub) const;
|
||||
void SetPublicKey (const uint8_t * pub);
|
||||
void Encaps (uint8_t * ciphertext, uint8_t * shared);
|
||||
void Decaps (const uint8_t * ciphertext, uint8_t * shared);
|
||||
|
||||
private:
|
||||
|
||||
const std::string m_Name;
|
||||
const size_t m_KeyLen, m_CTLen;
|
||||
EVP_PKEY * m_Pkey;
|
||||
};
|
||||
|
||||
std::unique_ptr<MLKEMKeys> CreateMLKEMKeys (i2p::data::CryptoKeyType type);
|
||||
|
||||
void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub); // Noise_IK (ratchets) PQ ML-KEM5
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -310,7 +310,7 @@ namespace data
|
|||
{
|
||||
if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL)
|
||||
{
|
||||
tmp.push_back (std::make_pair (it->first, it->second));
|
||||
tmp.push_back (*it);
|
||||
it->second->SetLastPersistTime (ts);
|
||||
it->second->SetUpdated (false);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace data
|
|||
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
|
||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes)
|
||||
const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours)
|
||||
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
|
||||
const int PEER_PROFILE_PERSIST_INTERVAL = 1320; // in seconds (22 minutes)
|
||||
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
|
||||
const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
|
||||
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Transports.h"
|
||||
#include "Tunnel.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -675,12 +676,12 @@ namespace i2p
|
|||
|
||||
void RouterContext::SetBandwidth (int limit)
|
||||
{
|
||||
if (limit > 2000) { SetBandwidth('X'); }
|
||||
else if (limit > 256) { SetBandwidth('P'); }
|
||||
else if (limit > 128) { SetBandwidth('O'); }
|
||||
else if (limit > 64) { SetBandwidth('N'); }
|
||||
else if (limit > 48) { SetBandwidth('M'); }
|
||||
else if (limit > 12) { SetBandwidth('L'); }
|
||||
if (limit > (int)i2p::data::EXTRA_BANDWIDTH_LIMIT) { SetBandwidth('X'); }
|
||||
else if (limit > (int)i2p::data::HIGH_BANDWIDTH_LIMIT) { SetBandwidth('P'); }
|
||||
else if (limit > 128) { SetBandwidth('O'); }
|
||||
else if (limit > 64) { SetBandwidth('N'); }
|
||||
else if (limit > (int)i2p::data::LOW_BANDWIDTH_LIMIT) { SetBandwidth('M'); }
|
||||
else if (limit > 12) { SetBandwidth('L'); }
|
||||
else { SetBandwidth('K'); }
|
||||
m_BandwidthLimit = limit; // set precise limit
|
||||
}
|
||||
|
@ -1193,7 +1194,8 @@ namespace i2p
|
|||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
|
||||
}
|
||||
|
||||
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
|
||||
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
if (typeID == eI2NPTunnelTest)
|
||||
{
|
||||
|
|
|
@ -204,7 +204,8 @@ namespace garlic
|
|||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "I2PEndian.h"
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <charconv>
|
||||
#include <boost/algorithm/string.hpp> // for boost::to_lower
|
||||
#ifndef __cpp_lib_atomic_shared_ptr
|
||||
#include <boost/atomic.hpp>
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include "Transports.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "RouterContext.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -106,8 +107,7 @@ namespace data
|
|||
// skip identity
|
||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
||||
// read new RI
|
||||
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen));
|
||||
ReadFromStream (str);
|
||||
ReadFromBuffer (buf + identityLen, len - identityLen);
|
||||
if (!m_IsUnreachable)
|
||||
UpdateBuffer (buf, len); // save buffer
|
||||
// don't delete buffer until saved to the file
|
||||
|
@ -195,39 +195,34 @@ namespace data
|
|||
}
|
||||
}
|
||||
// parse RI
|
||||
std::stringstream str;
|
||||
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
|
||||
ReadFromStream (str);
|
||||
if (!str)
|
||||
if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Malformed message");
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromStream (std::istream& s)
|
||||
bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (!s) return;
|
||||
if (len < 9) return false;
|
||||
m_Caps = 0; m_Congestion = eLowCongestion;
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
m_Timestamp = bufbe64toh (buf);
|
||||
size_t offset = 8; // timestamp
|
||||
// read addresses
|
||||
auto addresses = NewAddresses ();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
uint8_t numAddresses = buf[offset]; offset++;
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
if (offset + 9 > len) return false; // 1 byte cost + 8 bytes date
|
||||
uint8_t supportedTransports = 0;
|
||||
auto address = NewAddress ();
|
||||
uint8_t cost; // ignore
|
||||
s.read ((char *)&cost, sizeof (cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
offset++; // cost, ignore
|
||||
address->date = bufbe64toh (buf + offset); offset += 8; // date
|
||||
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
|
||||
char transportStyle[6];
|
||||
ReadString (transportStyle, 6, s);
|
||||
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||
auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
|
||||
if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
|
||||
address->transportStyle = eTransportNTCP2;
|
||||
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
|
||||
else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2
|
||||
{
|
||||
address->transportStyle = eTransportSSU2;
|
||||
address->ssu.reset (new SSUExt ());
|
||||
|
@ -237,24 +232,22 @@ namespace data
|
|||
address->transportStyle = eTransportUnknown;
|
||||
address->caps = 0;
|
||||
address->port = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
if (offset + 2 > len) return false;
|
||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
||||
if (offset + size >= len) return false;
|
||||
if (address->transportStyle == eTransportUnknown)
|
||||
{
|
||||
// skip unknown address
|
||||
s.seekg (size, std::ios_base::cur);
|
||||
if (s) continue; else return;
|
||||
offset += size;
|
||||
continue;
|
||||
}
|
||||
size_t r = 0;
|
||||
while (r < size)
|
||||
{
|
||||
char key[255], value[255];
|
||||
r += ReadString (key, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
if (!s) return;
|
||||
if (!strcmp (key, "host"))
|
||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
||||
r += sz; offset += sz;
|
||||
if (key.empty ()) continue;
|
||||
if (key == "host")
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address->host = boost::asio::ip::make_address (value, ecode);
|
||||
|
@ -268,63 +261,53 @@ namespace data
|
|||
address->transportStyle = eTransportUnknown;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
else if (key == "port")
|
||||
{
|
||||
try
|
||||
{
|
||||
address->port = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
|
||||
}
|
||||
auto res = std::from_chars(value.data(), value.data() + value.size(), address->port);
|
||||
if (res.ec != std::errc())
|
||||
LogPrint (eLogWarning, "RouterInfo: 'port' conversion error: ", std::make_error_code (res.ec).message ());
|
||||
}
|
||||
else if (!strcmp (key, "mtu"))
|
||||
else if (key == "mtu")
|
||||
{
|
||||
if (address->ssu)
|
||||
{
|
||||
try
|
||||
{
|
||||
address->ssu->mtu = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
|
||||
}
|
||||
auto res = std::from_chars(value.data(), value.data() + value.size(), address->ssu->mtu);
|
||||
if (res.ec != std::errc())
|
||||
LogPrint (eLogWarning, "RouterInfo: 'mtu' conversion error: ", std::make_error_code (res.ec).message ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
else if (key == "caps")
|
||||
address->caps = ExtractAddressCaps (value);
|
||||
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
|
||||
else if (key == "s") // ntcp2 or ssu2 static key
|
||||
{
|
||||
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 &&
|
||||
if (Base64ToByteStream (value, address->s, 32) == 32 &&
|
||||
!(address->s[31] & 0x80)) // check if x25519 public key
|
||||
isStaticKey = true;
|
||||
else
|
||||
address->transportStyle = eTransportUnknown; // invalid address
|
||||
}
|
||||
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
|
||||
else if (key == "i") // ntcp2 iv or ssu2 intro
|
||||
{
|
||||
if (address->IsNTCP2 ())
|
||||
{
|
||||
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16)
|
||||
if (Base64ToByteStream (value, address->i, 16) == 16)
|
||||
address->published = true; // presence of "i" means "published" NTCP2
|
||||
else
|
||||
address->transportStyle = eTransportUnknown; // invalid address
|
||||
}
|
||||
else if (address->IsSSU2 ())
|
||||
{
|
||||
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32)
|
||||
if (Base64ToByteStream (value, address->i, 32) == 32)
|
||||
isIntroKey = true;
|
||||
else
|
||||
address->transportStyle = eTransportUnknown; // invalid address
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "v"))
|
||||
else if (key == "v")
|
||||
{
|
||||
if (!strcmp (value, "2"))
|
||||
if (value == "2")
|
||||
isV2 = true;
|
||||
else
|
||||
{
|
||||
|
@ -340,13 +323,11 @@ namespace data
|
|||
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
|
||||
continue;
|
||||
}
|
||||
size_t l = strlen(key);
|
||||
unsigned char index = key[l-1] - '0'; // TODO:
|
||||
key[l-1] = 0;
|
||||
unsigned char index = key[key.length () - 1] - '0'; // TODO:
|
||||
if (index > 9)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
||||
if (s) continue; else return;
|
||||
continue;
|
||||
}
|
||||
if (index >= address->ssu->introducers.size ())
|
||||
{
|
||||
|
@ -355,34 +336,23 @@ namespace data
|
|||
address->ssu->introducers.resize (index + 1);
|
||||
}
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "itag"))
|
||||
auto key1 = key.substr(0, key.length () - 1);
|
||||
if (key1 == "itag")
|
||||
{
|
||||
try
|
||||
{
|
||||
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
|
||||
}
|
||||
auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iTag);
|
||||
if (res.ec != std::errc())
|
||||
LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ());
|
||||
}
|
||||
else if (!strcmp (key, "ih"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
|
||||
else if (!strcmp (key, "iexp"))
|
||||
else if (key1 == "ih")
|
||||
Base64ToByteStream (value, introducer.iH, 32);
|
||||
else if (key1 == "iexp")
|
||||
{
|
||||
try
|
||||
{
|
||||
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
|
||||
}
|
||||
auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp);
|
||||
if (res.ec != std::errc())
|
||||
LogPrint (eLogWarning, "RouterInfo: 'iexp' conversion error: ", std::make_error_code (res.ec).message ());
|
||||
}
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
}
|
||||
if (address->transportStyle == eTransportNTCP2)
|
||||
{
|
||||
if (isStaticKey)
|
||||
|
@ -446,45 +416,41 @@ namespace data
|
|||
boost::atomic_store (&m_Addresses, addresses);
|
||||
#endif
|
||||
// read peers
|
||||
uint8_t numPeers;
|
||||
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
||||
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
|
||||
if (offset + 1 > len) return false;
|
||||
uint8_t numPeers = buf[offset]; offset++; // num peers
|
||||
offset += numPeers*32; // TODO: read peers
|
||||
// read properties
|
||||
if (offset + 2 > len) return false;
|
||||
m_Version = 0;
|
||||
bool isNetId = false;
|
||||
std::string family;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
||||
if (offset + size > len) return false;
|
||||
size_t r = 0;
|
||||
while (r < size)
|
||||
{
|
||||
char key[255], value[255];
|
||||
r += ReadString (key, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
if (!s) return;
|
||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
||||
r += sz; offset += sz;
|
||||
if (key.empty ()) continue;
|
||||
SetProperty (key, value);
|
||||
|
||||
// extract caps
|
||||
if (!strcmp (key, "caps"))
|
||||
if (key == "caps")
|
||||
{
|
||||
ExtractCaps (value);
|
||||
m_IsFloodfill = IsDeclaredFloodfill ();
|
||||
}
|
||||
// extract version
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
|
||||
else if (key == ROUTER_INFO_PROPERTY_VERSION)
|
||||
{
|
||||
m_Version = 0;
|
||||
char * ch = value;
|
||||
while (*ch)
|
||||
for (auto ch: value)
|
||||
{
|
||||
if (*ch >= '0' && *ch <= '9')
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
m_Version *= 10;
|
||||
m_Version += (*ch - '0');
|
||||
m_Version += (ch - '0');
|
||||
}
|
||||
ch++;
|
||||
}
|
||||
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
|
||||
{
|
||||
|
@ -497,24 +463,26 @@ namespace data
|
|||
}
|
||||
}
|
||||
// check netId
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID))
|
||||
else if (key == ROUTER_INFO_PROPERTY_NETID)
|
||||
{
|
||||
isNetId = true;
|
||||
if (atoi (value) != i2p::context.GetNetID ())
|
||||
int netID;
|
||||
auto res = std::from_chars(value.data(), value.data() + value.size(), netID);
|
||||
if (res.ec != std::errc() || netID != i2p::context.GetNetID ())
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
}
|
||||
// family
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
||||
else if (key == ROUTER_INFO_PROPERTY_FAMILY)
|
||||
{
|
||||
family = value;
|
||||
boost::to_lower (family);
|
||||
}
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
||||
else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG)
|
||||
{
|
||||
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value))
|
||||
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) // TODO
|
||||
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
|
||||
else
|
||||
{
|
||||
|
@ -522,25 +490,24 @@ namespace data
|
|||
SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
if (!m_SupportedTransports || !isNetId || !m_Version)
|
||||
SetUnreachable (true);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsFamily (FamilyID famid) const
|
||||
{
|
||||
return m_FamilyID == famid;
|
||||
}
|
||||
|
||||
void RouterInfo::ExtractCaps (const char * value)
|
||||
void RouterInfo::ExtractCaps (std::string_view value)
|
||||
{
|
||||
const char * cap = value;
|
||||
while (*cap)
|
||||
for (auto cap: value)
|
||||
{
|
||||
switch (*cap)
|
||||
switch (cap)
|
||||
{
|
||||
case CAPS_FLAG_FLOODFILL:
|
||||
m_Caps |= Caps::eFloodfill;
|
||||
|
@ -549,16 +516,16 @@ namespace data
|
|||
case CAPS_FLAG_LOW_BANDWIDTH2:
|
||||
case CAPS_FLAG_LOW_BANDWIDTH3:
|
||||
case CAPS_FLAG_LOW_BANDWIDTH4:
|
||||
m_BandwidthCap = *cap;
|
||||
m_BandwidthCap = cap;
|
||||
break;
|
||||
case CAPS_FLAG_HIGH_BANDWIDTH:
|
||||
m_Caps |= Caps::eHighBandwidth;
|
||||
m_BandwidthCap = *cap;
|
||||
m_BandwidthCap = cap;
|
||||
break;
|
||||
case CAPS_FLAG_EXTRA_BANDWIDTH1:
|
||||
case CAPS_FLAG_EXTRA_BANDWIDTH2:
|
||||
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
|
||||
m_BandwidthCap = *cap;
|
||||
m_BandwidthCap = cap;
|
||||
break;
|
||||
case CAPS_FLAG_HIDDEN:
|
||||
m_Caps |= Caps::eHidden;
|
||||
|
@ -580,17 +547,15 @@ namespace data
|
|||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
|
||||
}
|
||||
|
||||
uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const
|
||||
{
|
||||
uint8_t caps = 0;
|
||||
const char * cap = value;
|
||||
while (*cap)
|
||||
for (auto cap: value)
|
||||
{
|
||||
switch (*cap)
|
||||
switch (cap)
|
||||
{
|
||||
case CAPS_FLAG_V4:
|
||||
caps |= AddressCaps::eV4;
|
||||
|
@ -606,11 +571,10 @@ namespace data
|
|||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts)
|
||||
{
|
||||
if (!address || !address->ssu) return;
|
||||
|
@ -670,25 +634,41 @@ namespace data
|
|||
return SaveToFile (fullPath, m_Buffer);
|
||||
}
|
||||
|
||||
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
|
||||
std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
uint8_t l;
|
||||
s.read ((char *)&l, 1);
|
||||
if (l < len)
|
||||
{
|
||||
s.read (str, l);
|
||||
if (!s) l = 0; // failed, return empty string
|
||||
str[l] = 0;
|
||||
}
|
||||
else
|
||||
uint8_t l = buf[0];
|
||||
if (l > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
|
||||
s.seekg (l, std::ios::cur); // skip
|
||||
str[0] = 0;
|
||||
}
|
||||
return l+1;
|
||||
l = len;
|
||||
}
|
||||
return { (const char *)(buf + 1), l };
|
||||
}
|
||||
|
||||
std::tuple<std::string_view, std::string_view, size_t> RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
auto key = ExtractString (buf, len);
|
||||
size_t offset = key.length () + 1;
|
||||
if (offset >= len) return { std::string_view(), std::string_view(), len };
|
||||
if (buf[offset] != '=')
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key);
|
||||
key = std::string_view();
|
||||
}
|
||||
offset++;
|
||||
if (offset >= len) return { key, std::string_view(), len };
|
||||
auto value = ExtractString (buf + offset, len - offset);
|
||||
offset += value.length () + 1;
|
||||
if (offset >= len) return { key, std::string_view(), len };
|
||||
if (buf[offset] != ';')
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value);
|
||||
value = std::string_view();
|
||||
}
|
||||
offset++;
|
||||
return { key, value, offset };
|
||||
}
|
||||
|
||||
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
|
||||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
|
@ -1402,9 +1382,9 @@ namespace data
|
|||
if (!introducer.iTag) continue;
|
||||
if (introducer.iExp) // expiration is specified
|
||||
{
|
||||
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
|
||||
WriteString ("iexp" + std::to_string(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
|
||||
WriteString (std::to_string(introducer.iExp), properties);
|
||||
properties << ';';
|
||||
}
|
||||
i++;
|
||||
|
@ -1413,11 +1393,9 @@ namespace data
|
|||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
if (!introducer.iTag) continue;
|
||||
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||
WriteString ("ih" + std::to_string(i), properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
|
||||
value[l] = 0;
|
||||
auto value = ByteStreamToBase64 (introducer.iH, 32);
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
|
@ -1426,9 +1404,9 @@ namespace data
|
|||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
if (!introducer.iTag) continue;
|
||||
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
||||
WriteString ("itag" + std::to_string(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
|
||||
WriteString (std::to_string(introducer.iTag), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
|
@ -1442,7 +1420,7 @@ namespace data
|
|||
{
|
||||
WriteString ("mtu", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.ssu->mtu), properties);
|
||||
WriteString (std::to_string(address.ssu->mtu), properties);
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
|
@ -1450,7 +1428,7 @@ namespace data
|
|||
{
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
||||
WriteString (std::to_string(address.port), properties);
|
||||
properties << ';';
|
||||
}
|
||||
if (address.IsNTCP2 () || address.IsSSU2 ())
|
||||
|
@ -1485,9 +1463,11 @@ namespace data
|
|||
s.write (properties.str ().c_str (), properties.str ().size ());
|
||||
}
|
||||
|
||||
void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value)
|
||||
void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value)
|
||||
{
|
||||
m_Properties[key] = value;
|
||||
auto [it, inserted] = m_Properties.emplace (key, value);
|
||||
if (!inserted)
|
||||
it->second = value;
|
||||
}
|
||||
|
||||
void LocalRouterInfo::DeleteProperty (const std::string& key)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
@ -208,8 +210,8 @@ namespace data
|
|||
typedef boost::shared_ptr<Addresses> AddressesPtr;
|
||||
#endif
|
||||
RouterInfo (const std::string& fullPath);
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
RouterInfo& operator=(const RouterInfo& ) = default;
|
||||
RouterInfo (const RouterInfo& ) = delete;
|
||||
RouterInfo& operator=(const RouterInfo& ) = delete;
|
||||
RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len);
|
||||
RouterInfo (const uint8_t * buf, size_t len);
|
||||
virtual ~RouterInfo ();
|
||||
|
@ -219,7 +221,7 @@ namespace data
|
|||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
int GetVersion () const { return m_Version; };
|
||||
virtual void SetProperty (const std::string& key, const std::string& value) {};
|
||||
virtual void SetProperty (std::string_view key, std::string_view value) {};
|
||||
virtual void ClearProperties () {};
|
||||
AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCP2V4Address () const;
|
||||
|
@ -333,11 +335,12 @@ namespace data
|
|||
|
||||
bool LoadFile (const std::string& fullPath);
|
||||
void ReadFromFile (const std::string& fullPath);
|
||||
void ReadFromStream (std::istream& s);
|
||||
bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed
|
||||
void ReadFromBuffer (bool verifySignature);
|
||||
size_t ReadString (char* str, size_t len, std::istream& s) const;
|
||||
void ExtractCaps (const char * value);
|
||||
uint8_t ExtractAddressCaps (const char * value) const;
|
||||
std::string_view ExtractString (const uint8_t * buf, size_t len) const;
|
||||
std::tuple<std::string_view, std::string_view, size_t> ExtractParam (const uint8_t * buf, size_t len) const;
|
||||
void ExtractCaps (std::string_view value);
|
||||
uint8_t ExtractAddressCaps (std::string_view value) const;
|
||||
void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||
|
@ -379,7 +382,7 @@ namespace data
|
|||
void UpdateCaps (uint8_t caps);
|
||||
bool UpdateCongestion (Congestion c); // returns true if updated
|
||||
|
||||
void SetProperty (const std::string& key, const std::string& value) override;
|
||||
void SetProperty (std::string_view key, std::string_view value) override;
|
||||
void DeleteProperty (const std::string& key);
|
||||
std::string GetProperty (const std::string& key) const;
|
||||
void ClearProperties () override { m_Properties.clear (); };
|
||||
|
|
|
@ -1251,18 +1251,21 @@ namespace transport
|
|||
}
|
||||
uint64_t token;
|
||||
RAND_bytes ((uint8_t *)&token, 8);
|
||||
m_IncomingTokens.emplace (ep, std::make_pair (token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)));
|
||||
if (!token) token = 1; // token can't be zero
|
||||
m_IncomingTokens.try_emplace (ep, token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT));
|
||||
return token;
|
||||
}
|
||||
|
||||
std::pair<uint64_t, uint32_t> SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep)
|
||||
{
|
||||
m_IncomingTokens.erase (ep); // drop previous
|
||||
uint64_t token;
|
||||
RAND_bytes ((uint8_t *)&token, 8);
|
||||
auto ret = std::make_pair (token, uint32_t(i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT));
|
||||
m_IncomingTokens.emplace (ep, ret);
|
||||
return ret;
|
||||
if (!token) token = 1; // token can't be zero
|
||||
uint32_t expires = i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT;
|
||||
auto [it, inserted] = m_IncomingTokens.try_emplace (ep, token, expires);
|
||||
if (!inserted)
|
||||
it->second = { token, expires }; // override
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
|
||||
|
|
|
@ -191,12 +191,7 @@ namespace transport
|
|||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
m_SignedData.assign (signedData, signedData + signedDataLen);
|
||||
#else
|
||||
m_SignedData.resize (signedDataLen);
|
||||
memcpy (m_SignedData.data (), signedData, signedDataLen);
|
||||
#endif
|
||||
if (!delayed)
|
||||
SendPeerTest (msg);
|
||||
// schedule resend for msgs 5 or 6
|
||||
|
@ -257,7 +252,7 @@ namespace transport
|
|||
{
|
||||
// we are Charlie
|
||||
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
|
||||
uint32_t sourceConnID = ~destConnID;
|
||||
uint64_t sourceConnID = ~destConnID;
|
||||
SetSourceConnID (sourceConnID);
|
||||
SetDestConnID (destConnID);
|
||||
SetState (eSSU2SessionStateHolePunch);
|
||||
|
@ -313,12 +308,7 @@ namespace transport
|
|||
|
||||
void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen);
|
||||
#else
|
||||
m_RelayResponseBlock.resize (relayResponseBlockLen);
|
||||
memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen);
|
||||
#endif
|
||||
SendHolePunch ();
|
||||
ScheduleResend ();
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ namespace transport
|
|||
if (!asz) return false;
|
||||
payload[17] = asz;
|
||||
packet->payloadSize = asz + 18;
|
||||
SignedData s;
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash
|
||||
|
@ -1498,11 +1498,11 @@ namespace transport
|
|||
ResendHandshakePacket (); // assume we receive
|
||||
return;
|
||||
}
|
||||
if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ()))
|
||||
if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ()) &&
|
||||
(!m_PathChallenge || from != m_PathChallenge->second)) // path challenge was not sent to this endpoint yet
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from);
|
||||
m_RemoteEndpoint = from;
|
||||
SendPathChallenge ();
|
||||
SendPathChallenge (from);
|
||||
}
|
||||
if (len < 32)
|
||||
{
|
||||
|
@ -1660,10 +1660,11 @@ namespace transport
|
|||
LogPrint (eLogDebug, "SSU2: Path response");
|
||||
if (m_PathChallenge)
|
||||
{
|
||||
i2p::data::IdentHash hash;
|
||||
SHA256 (buf + offset, size, hash);
|
||||
if (hash == *m_PathChallenge)
|
||||
if (buf64toh (buf + offset) == m_PathChallenge->first)
|
||||
{
|
||||
m_RemoteEndpoint = m_PathChallenge->second;
|
||||
m_PathChallenge.reset (nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1965,6 +1966,7 @@ namespace transport
|
|||
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Bob
|
||||
if (len < 9) return;
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint32_t nonce = bufbe32toh (buf + 1); // nonce
|
||||
uint32_t relayTag = bufbe32toh (buf + 5); // relay tag
|
||||
|
@ -1998,7 +2000,7 @@ namespace transport
|
|||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len - 1);
|
||||
if (packet->payloadSize < m_MaxPayloadSize)
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
|
||||
|
@ -2013,18 +2015,24 @@ namespace transport
|
|||
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
|
||||
{
|
||||
// we are Charlie
|
||||
if (len < 47) return;
|
||||
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
|
||||
boost::asio::ip::udp::endpoint ep;
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
|
||||
auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice
|
||||
if (r)
|
||||
{
|
||||
SignedData s;
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (i2p::context.GetIdentHash (), 32); // chash
|
||||
s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz
|
||||
uint8_t asz = buf[46];
|
||||
if (asz + 47 + r->GetIdentity ()->GetSignatureLen () > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Malformed RelayIntro len=", len);
|
||||
return;
|
||||
}
|
||||
s.Insert (buf + 47, asz); // Alice Port, Alice IP
|
||||
if (s.Verify (r->GetIdentity (), buf + 47 + asz))
|
||||
{
|
||||
|
@ -2113,6 +2121,7 @@ namespace transport
|
|||
|
||||
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < 6) return;
|
||||
uint32_t nonce = bufbe32toh (buf + 2);
|
||||
if (m_State == eSSU2SessionStateIntroduced)
|
||||
{
|
||||
|
@ -2133,7 +2142,9 @@ namespace transport
|
|||
auto it = m_RelaySessions.find (nonce);
|
||||
if (it != m_RelaySessions.end ())
|
||||
{
|
||||
if (it->second.first && it->second.first->IsEstablished ())
|
||||
auto relaySession = it->second.first;
|
||||
m_RelaySessions.erase (it);
|
||||
if (relaySession && relaySession->IsEstablished ())
|
||||
{
|
||||
// we are Bob, message from Charlie
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
|
@ -2143,12 +2154,12 @@ namespace transport
|
|||
memcpy (payload + 3, buf, len); // forward to Alice as is
|
||||
packet->payloadSize = len + 3;
|
||||
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = it->second.first->SendData (packet->payload, packet->payloadSize);
|
||||
uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Alice doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
it->second.first->m_SentPackets.emplace (packetNum, packet);
|
||||
relaySession->m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2157,25 +2168,31 @@ namespace transport
|
|||
if (!buf[1]) // status code accepted?
|
||||
{
|
||||
// verify signature
|
||||
uint8_t csz = buf[11];
|
||||
SignedData s;
|
||||
uint8_t csz = (len >= 12) ? buf[11] : 0;
|
||||
if (csz + 12 + relaySession->GetRemoteIdentity ()->GetSignatureLen () > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Malformed RelayResponse len=", len);
|
||||
relaySession->Done ();
|
||||
return;
|
||||
}
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint
|
||||
if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz))
|
||||
if (s.Verify (relaySession->GetRemoteIdentity (), buf + 12 + csz))
|
||||
{
|
||||
if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet
|
||||
if (relaySession->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet
|
||||
{
|
||||
// update Charlie's endpoint
|
||||
if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint))
|
||||
if (ExtractEndpoint (buf + 12, csz, relaySession->m_RemoteEndpoint))
|
||||
{
|
||||
// update token
|
||||
uint64_t token;
|
||||
memcpy (&token, buf + len - 8, 8);
|
||||
m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint,
|
||||
m_Server.UpdateOutgoingToken (relaySession->m_RemoteEndpoint,
|
||||
token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT);
|
||||
// connect to Charlie, HolePunch will be ignored
|
||||
it->second.first->ConnectAfterIntroduction ();
|
||||
relaySession->ConnectAfterIntroduction ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint");
|
||||
|
@ -2184,16 +2201,15 @@ namespace transport
|
|||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed");
|
||||
it->second.first->Done ();
|
||||
relaySession->Done ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2));
|
||||
it->second.first->Done ();
|
||||
relaySession->Done ();
|
||||
}
|
||||
}
|
||||
m_RelaySessions.erase (it);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2));
|
||||
|
@ -2262,10 +2278,13 @@ namespace transport
|
|||
case 2: // Charlie from Bob
|
||||
{
|
||||
// sign with Charlie's key
|
||||
if (len < offset + 9) return;
|
||||
uint8_t asz = buf[offset + 9];
|
||||
std::vector<uint8_t> newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ());
|
||||
size_t l = asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ();
|
||||
if (len < offset + l) return;
|
||||
std::vector<uint8_t> newSignedData (l);
|
||||
memcpy (newSignedData.data (), buf + offset, asz + 10);
|
||||
SignedData s;
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (buf + 3, 32); // ahash
|
||||
|
@ -2373,10 +2392,16 @@ namespace transport
|
|||
if (GetRouterStatus () == eRouterStatusUnknown)
|
||||
SetTestingState (true);
|
||||
auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie
|
||||
if (r)
|
||||
if (r && len >= offset + 9)
|
||||
{
|
||||
uint8_t asz = buf[offset + 9];
|
||||
SignedData s;
|
||||
if (len < offset + asz + 10 + r->GetIdentity ()->GetSignatureLen ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Malformed PeerTest 4 len=", len);
|
||||
session->Done ();
|
||||
return;
|
||||
}
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash
|
||||
|
@ -2535,17 +2560,19 @@ namespace transport
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void SSU2Session::AdjustMaxPayloadSize ()
|
||||
void SSU2Session::AdjustMaxPayloadSize (size_t maxMtu)
|
||||
{
|
||||
auto addr = FindLocalAddress ();
|
||||
if (addr && addr->ssu)
|
||||
{
|
||||
int mtu = addr->ssu->mtu;
|
||||
if (!mtu && addr->IsV4 ()) mtu = SSU2_MAX_PACKET_SIZE;
|
||||
if (mtu > (int)maxMtu) mtu = maxMtu;
|
||||
if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu))
|
||||
mtu = m_Address->ssu->mtu;
|
||||
if (mtu)
|
||||
{
|
||||
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
|
||||
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
|
||||
m_MaxPayloadSize = mtu - (addr->IsV6 () ? IPV6_HEADER_SIZE: IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32;
|
||||
LogPrint (eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize);
|
||||
|
@ -2762,7 +2789,7 @@ namespace transport
|
|||
size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize)
|
||||
{
|
||||
if (len < 3 || len < minSize) return 0;
|
||||
size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15
|
||||
size_t paddingSize = m_Server.GetRng ()() & 0x1F; // 0 - 31
|
||||
if (paddingSize + 3 > len) paddingSize = len - 3;
|
||||
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
|
||||
buf[0] = eSSU2BlkPadding;
|
||||
|
@ -2864,7 +2891,7 @@ namespace transport
|
|||
LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len);
|
||||
return 0;
|
||||
}
|
||||
SignedData s;
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
|
||||
if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
|
@ -2926,7 +2953,7 @@ namespace transport
|
|||
size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port));
|
||||
signedData[9] = asz;
|
||||
// signature
|
||||
SignedData s;
|
||||
SignedData<128> s;
|
||||
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
|
||||
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
|
||||
s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint
|
||||
|
@ -3031,38 +3058,67 @@ namespace transport
|
|||
|
||||
void SSU2Session::SendPathResponse (const uint8_t * data, size_t len)
|
||||
{
|
||||
if (len > m_MaxPayloadSize - 3)
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
size_t payloadSize = 0;
|
||||
// datetime block
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
payloadSize += 7;
|
||||
// address block
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint);
|
||||
// path response
|
||||
if (payloadSize + len > m_MaxPayloadSize)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len);
|
||||
return;
|
||||
}
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
payload[0] = eSSU2BlkPathResponse;
|
||||
htobe16buf (payload + 1, len);
|
||||
memcpy (payload + 3, data, len);
|
||||
size_t payloadSize = len + 3;
|
||||
}
|
||||
payload[payloadSize] = eSSU2BlkPathResponse;
|
||||
htobe16buf (payload + payloadSize + 1, len);
|
||||
memcpy (payload + payloadSize + 3, data, len);
|
||||
payloadSize += len + 3;
|
||||
// ack block
|
||||
if (payloadSize < m_MaxPayloadSize)
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, payloadSize < 8 ? 8 : 0);
|
||||
payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
// padding
|
||||
if (payloadSize < m_MaxPayloadSize)
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
SendData (payload, payloadSize);
|
||||
}
|
||||
|
||||
void SSU2Session::SendPathChallenge ()
|
||||
void SSU2Session::SendPathChallenge (const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
AdjustMaxPayloadSize (SSU2_MIN_PACKET_SIZE); // reduce to minimum
|
||||
m_WindowSize = SSU2_MIN_WINDOW_SIZE; // reduce window to minimum
|
||||
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
payload[0] = eSSU2BlkPathChallenge;
|
||||
size_t len = m_Server.GetRng ()() % (m_MaxPayloadSize - 3);
|
||||
htobe16buf (payload + 1, len);
|
||||
if (len > 0)
|
||||
{
|
||||
RAND_bytes (payload + 3, len);
|
||||
i2p::data::IdentHash * hash = new i2p::data::IdentHash ();
|
||||
SHA256 (payload + 3, len, *hash);
|
||||
m_PathChallenge.reset (hash);
|
||||
}
|
||||
len += 3;
|
||||
if (len < m_MaxPayloadSize)
|
||||
len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len, len < 8 ? 8 : 0);
|
||||
SendData (payload, len);
|
||||
size_t payloadSize = 0;
|
||||
// datetime block
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
payloadSize += 7;
|
||||
// address block with new address
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, to);
|
||||
// path challenge block
|
||||
payload[payloadSize] = eSSU2BlkPathChallenge;
|
||||
uint64_t challenge;
|
||||
RAND_bytes ((uint8_t *)&challenge, 8);
|
||||
htobe16buf (payload + payloadSize + 1, 8); // always 8 bytes
|
||||
htobuf64 (payload + payloadSize + 3, challenge);
|
||||
payloadSize += 11;
|
||||
m_PathChallenge = std::make_unique<std::pair<uint64_t, boost::asio::ip::udp::endpoint> >(challenge, to);
|
||||
// ack block
|
||||
if (payloadSize < m_MaxPayloadSize)
|
||||
payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
// padding block
|
||||
if (payloadSize < m_MaxPayloadSize)
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
// send to new endpoint
|
||||
auto existing = m_RemoteEndpoint;
|
||||
m_RemoteEndpoint = to; // send path challenge to new endpoint
|
||||
SendData (payload, payloadSize);
|
||||
m_RemoteEndpoint = existing; // restore endpoint back until path response received
|
||||
}
|
||||
|
||||
void SSU2Session::CleanUp (uint64_t ts)
|
||||
|
|
|
@ -327,7 +327,7 @@ namespace transport
|
|||
void SendQuickAck ();
|
||||
void SendTermination ();
|
||||
void SendPathResponse (const uint8_t * data, size_t len);
|
||||
void SendPathChallenge ();
|
||||
void SendPathChallenge (const boost::asio::ip::udp::endpoint& to);
|
||||
|
||||
void HandleDateTime (const uint8_t * buf, size_t len);
|
||||
void HandleRouterInfo (const uint8_t * buf, size_t len);
|
||||
|
@ -336,7 +336,7 @@ namespace transport
|
|||
virtual void HandleAddress (const uint8_t * buf, size_t len);
|
||||
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
|
||||
void AdjustMaxPayloadSize ();
|
||||
void AdjustMaxPayloadSize (size_t maxMtu = SSU2_MAX_PACKET_SIZE);
|
||||
bool GetTestingState () const;
|
||||
void SetTestingState(bool testing) const;
|
||||
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
|
||||
|
@ -394,7 +394,7 @@ namespace transport
|
|||
boost::asio::deadline_timer m_ConnectTimer;
|
||||
SSU2TerminationReason m_TerminationReason;
|
||||
size_t m_MaxPayloadSize;
|
||||
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
|
||||
std::unique_ptr<std::pair<uint64_t, boost::asio::ip::udp::endpoint> > m_PathChallenge;
|
||||
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
|
||||
uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds
|
||||
int m_NumRanges;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -7,6 +7,10 @@
|
|||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
#include <openssl/core_names.h>
|
||||
#endif
|
||||
#include "Log.h"
|
||||
#include "Signature.h"
|
||||
|
||||
|
@ -14,6 +18,163 @@ namespace i2p
|
|||
{
|
||||
namespace crypto
|
||||
{
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
DSAVerifier::DSAVerifier ():
|
||||
m_PublicKey (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DSAVerifier::~DSAVerifier ()
|
||||
{
|
||||
if (m_PublicKey)
|
||||
EVP_PKEY_free (m_PublicKey);
|
||||
}
|
||||
|
||||
void DSAVerifier::SetPublicKey (const uint8_t * signingKey)
|
||||
{
|
||||
if (m_PublicKey)
|
||||
EVP_PKEY_free (m_PublicKey);
|
||||
BIGNUM * pub = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
|
||||
m_PublicKey = CreateDSA (pub);
|
||||
BN_free (pub);
|
||||
}
|
||||
|
||||
bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
// calculate SHA1 digest
|
||||
uint8_t digest[20], sign[48];
|
||||
SHA1 (buf, len, digest);
|
||||
// signature
|
||||
DSA_SIG * sig = DSA_SIG_new();
|
||||
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
|
||||
// to DER format
|
||||
uint8_t * s = sign;
|
||||
auto l = i2d_DSA_SIG (sig, &s);
|
||||
DSA_SIG_free(sig);
|
||||
// verify
|
||||
auto ctx = EVP_PKEY_CTX_new (m_PublicKey, NULL);
|
||||
EVP_PKEY_verify_init(ctx);
|
||||
EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1());
|
||||
bool ret = EVP_PKEY_verify(ctx, sign, l, digest, 20);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
{
|
||||
BIGNUM * priv = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
|
||||
m_PrivateKey = CreateDSA (nullptr, priv);
|
||||
BN_free (priv);
|
||||
}
|
||||
|
||||
DSASigner::~DSASigner ()
|
||||
{
|
||||
if (m_PrivateKey)
|
||||
EVP_PKEY_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[20], sign[48];
|
||||
SHA1 (buf, len, digest);
|
||||
auto ctx = EVP_PKEY_CTX_new (m_PrivateKey, NULL);
|
||||
EVP_PKEY_sign_init(ctx);
|
||||
EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1());
|
||||
size_t l = 48;
|
||||
EVP_PKEY_sign(ctx, sign, &l, digest, 20);
|
||||
const uint8_t * s1 = sign;
|
||||
DSA_SIG * sig = d2i_DSA_SIG (NULL, &s1, l);
|
||||
const BIGNUM * r, * s;
|
||||
DSA_SIG_get0 (sig, &r, &s);
|
||||
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
DSA_SIG_free(sig);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
}
|
||||
|
||||
void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
EVP_PKEY * paramskey = CreateDSA();
|
||||
EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_pkey(NULL, paramskey, NULL);
|
||||
EVP_PKEY_keygen_init(ctx);
|
||||
EVP_PKEY * pkey = nullptr;
|
||||
EVP_PKEY_keygen(ctx, &pkey);
|
||||
BIGNUM * pub = NULL, * priv = NULL;
|
||||
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub);
|
||||
bn2buf (pub, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv);
|
||||
bn2buf (priv, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
BN_free (pub); BN_free (priv);
|
||||
EVP_PKEY_free (pkey);
|
||||
EVP_PKEY_free (paramskey);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
#else
|
||||
|
||||
DSAVerifier::DSAVerifier ()
|
||||
{
|
||||
m_PublicKey = CreateDSA ();
|
||||
}
|
||||
|
||||
DSAVerifier::~DSAVerifier ()
|
||||
{
|
||||
DSA_free (m_PublicKey);
|
||||
}
|
||||
|
||||
void DSAVerifier::SetPublicKey (const uint8_t * signingKey)
|
||||
{
|
||||
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
|
||||
}
|
||||
|
||||
bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
// calculate SHA1 digest
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
// signature
|
||||
DSA_SIG * sig = DSA_SIG_new();
|
||||
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
|
||||
// DSA verification
|
||||
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
|
||||
DSA_SIG_free(sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
{
|
||||
m_PrivateKey = CreateDSA ();
|
||||
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
|
||||
}
|
||||
|
||||
DSASigner::~DSASigner ()
|
||||
{
|
||||
DSA_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
|
||||
const BIGNUM * r, * s;
|
||||
DSA_SIG_get0 (sig, &r, &s);
|
||||
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
DSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
DSA * dsa = CreateDSA ();
|
||||
DSA_generate_key (dsa);
|
||||
const BIGNUM * pub_key, * priv_key;
|
||||
DSA_get0_key(dsa, &pub_key, &priv_key);
|
||||
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENSSL_EDDSA
|
||||
EDDSA25519Verifier::EDDSA25519Verifier ():
|
||||
m_Pkey (nullptr)
|
||||
|
@ -149,5 +310,178 @@ namespace crypto
|
|||
LogPrint (eLogError, "EdDSA signing key is not set");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000)
|
||||
static const OSSL_PARAM EDDSA25519phParams[] =
|
||||
{
|
||||
OSSL_PARAM_utf8_string ("instance", (char *)"Ed25519ph", 9),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
|
||||
bool EDDSA25519phVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
auto pkey = GetPkey ();
|
||||
if (pkey)
|
||||
{
|
||||
uint8_t digest[64];
|
||||
SHA512 (buf, len, digest);
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestVerifyInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams);
|
||||
auto ret = EVP_DigestVerify (ctx, signature, 64, digest, 64);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "EdDSA verification key is not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
EDDSA25519phSigner::EDDSA25519phSigner (const uint8_t * signingPrivateKey):
|
||||
EDDSA25519Signer (signingPrivateKey)
|
||||
{
|
||||
}
|
||||
|
||||
void EDDSA25519phSigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
auto pkey = GetPkey ();
|
||||
if (pkey)
|
||||
{
|
||||
uint8_t digest[64];
|
||||
SHA512 (buf, len, digest);
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
size_t l = 64;
|
||||
uint8_t sig[64];
|
||||
EVP_DigestSignInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams);
|
||||
if (!EVP_DigestSign (ctx, sig, &l, digest, 64))
|
||||
LogPrint (eLogError, "EdDSA signing failed");
|
||||
memcpy (signature, sig, 64);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "EdDSA signing key is not set");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENSSL_PQ
|
||||
|
||||
MLDSA44Verifier::MLDSA44Verifier ():
|
||||
m_Pkey (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
MLDSA44Verifier::~MLDSA44Verifier ()
|
||||
{
|
||||
EVP_PKEY_free (m_Pkey);
|
||||
}
|
||||
|
||||
void MLDSA44Verifier::SetPublicKey (const uint8_t * signingKey)
|
||||
{
|
||||
if (m_Pkey)
|
||||
{
|
||||
EVP_PKEY_free (m_Pkey);
|
||||
m_Pkey = nullptr;
|
||||
}
|
||||
OSSL_PARAM params[] =
|
||||
{
|
||||
OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)signingKey, GetPublicKeyLen ()),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL);
|
||||
if (ctx)
|
||||
{
|
||||
EVP_PKEY_fromdata_init (ctx);
|
||||
EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 can't create PKEY context");
|
||||
}
|
||||
|
||||
bool MLDSA44Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_Pkey)
|
||||
{
|
||||
EVP_PKEY_CTX * vctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
||||
if (vctx)
|
||||
{
|
||||
EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL);
|
||||
if (sig)
|
||||
{
|
||||
int encode = 1;
|
||||
OSSL_PARAM params[] =
|
||||
{
|
||||
OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
EVP_PKEY_verify_message_init (vctx, sig, params);
|
||||
ret = EVP_PKEY_verify (vctx, signature, GetSignatureLen (), buf, len);
|
||||
EVP_SIGNATURE_free (sig);
|
||||
}
|
||||
EVP_PKEY_CTX_free (vctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 verification key is not set");
|
||||
return ret;
|
||||
}
|
||||
|
||||
MLDSA44Signer::MLDSA44Signer (const uint8_t * signingPrivateKey):
|
||||
m_Pkey (nullptr)
|
||||
{
|
||||
OSSL_PARAM params[] =
|
||||
{
|
||||
OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PRIV_KEY, (uint8_t *)signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL);
|
||||
if (ctx)
|
||||
{
|
||||
EVP_PKEY_fromdata_init (ctx);
|
||||
EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, params);
|
||||
EVP_PKEY_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 can't create PKEY context");
|
||||
}
|
||||
|
||||
MLDSA44Signer::~MLDSA44Signer ()
|
||||
{
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
}
|
||||
|
||||
void MLDSA44Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
if (m_Pkey)
|
||||
{
|
||||
EVP_PKEY_CTX * sctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
||||
if (sctx)
|
||||
{
|
||||
EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL);
|
||||
if (sig)
|
||||
{
|
||||
int encode = 1;
|
||||
OSSL_PARAM params[] =
|
||||
{
|
||||
OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
EVP_PKEY_sign_message_init (sctx, sig, params);
|
||||
size_t siglen = MLDSA44_SIGNATURE_LENGTH;
|
||||
EVP_PKEY_sign (sctx, signature, &siglen, buf, len);
|
||||
EVP_SIGNATURE_free (sig);
|
||||
}
|
||||
EVP_PKEY_CTX_free (sctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "MLDSA44 signing key is not set");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -43,94 +43,55 @@ namespace crypto
|
|||
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
||||
};
|
||||
|
||||
// DSA
|
||||
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
|
||||
const size_t DSA_SIGNATURE_LENGTH = 40;
|
||||
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2;
|
||||
class DSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
DSAVerifier ();
|
||||
~DSAVerifier ();
|
||||
|
||||
DSAVerifier ()
|
||||
{
|
||||
m_PublicKey = CreateDSA ();
|
||||
}
|
||||
|
||||
void SetPublicKey (const uint8_t * signingKey)
|
||||
{
|
||||
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
|
||||
}
|
||||
|
||||
~DSAVerifier ()
|
||||
{
|
||||
DSA_free (m_PublicKey);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
// calculate SHA1 digest
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
// signature
|
||||
DSA_SIG * sig = DSA_SIG_new();
|
||||
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
|
||||
// DSA verification
|
||||
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
|
||||
DSA_SIG_free(sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
|
||||
|
||||
// implements Verifier
|
||||
void SetPublicKey (const uint8_t * signingKey) override;
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const override;
|
||||
size_t GetPublicKeyLen () const override { return DSA_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const override { return DSA_SIGNATURE_LENGTH; };
|
||||
|
||||
private:
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * m_PublicKey;
|
||||
#else
|
||||
DSA * m_PublicKey;
|
||||
#endif
|
||||
};
|
||||
|
||||
class DSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey);
|
||||
// openssl 1.1 always requires DSA public key even for signing
|
||||
{
|
||||
m_PrivateKey = CreateDSA ();
|
||||
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
|
||||
}
|
||||
~DSASigner ();
|
||||
|
||||
~DSASigner ()
|
||||
{
|
||||
DSA_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
|
||||
const BIGNUM * r, * s;
|
||||
DSA_SIG_get0 (sig, &r, &s);
|
||||
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
DSA_SIG_free(sig);
|
||||
}
|
||||
// implements Signer
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const override;
|
||||
|
||||
private:
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * m_PrivateKey;
|
||||
#else
|
||||
DSA * m_PrivateKey;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
DSA * dsa = CreateDSA ();
|
||||
DSA_generate_key (dsa);
|
||||
const BIGNUM * pub_key, * priv_key;
|
||||
DSA_get0_key(dsa, &pub_key, &priv_key);
|
||||
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
}
|
||||
void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey);
|
||||
|
||||
// ECDSA
|
||||
struct SHA256Hash
|
||||
{
|
||||
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
|
||||
|
@ -161,7 +122,6 @@ namespace crypto
|
|||
enum { hashLen = 64 };
|
||||
};
|
||||
|
||||
// EcDSA
|
||||
template<typename Hash, int curve, size_t keyLen>
|
||||
class ECDSAVerifier: public Verifier
|
||||
{
|
||||
|
@ -303,14 +263,28 @@ namespace crypto
|
|||
|
||||
private:
|
||||
|
||||
#if OPENSSL_EDDSA
|
||||
#if OPENSSL_EDDSA
|
||||
|
||||
EVP_PKEY * m_Pkey;
|
||||
|
||||
protected:
|
||||
|
||||
EVP_PKEY * GetPkey () const { return m_Pkey; };
|
||||
#else
|
||||
EDDSAPoint m_PublicKey;
|
||||
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
|
||||
#endif
|
||||
};
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
class EDDSA25519phVerifier: public EDDSA25519Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
||||
};
|
||||
#endif
|
||||
|
||||
class EDDSA25519SignerCompat: public Signer
|
||||
{
|
||||
public:
|
||||
|
@ -339,6 +313,10 @@ namespace crypto
|
|||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
|
||||
protected:
|
||||
|
||||
EVP_PKEY * GetPkey () const { return m_Pkey; };
|
||||
|
||||
private:
|
||||
|
||||
EVP_PKEY * m_Pkey;
|
||||
|
@ -350,6 +328,18 @@ namespace crypto
|
|||
|
||||
#endif
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
class EDDSA25519phSigner: public EDDSA25519Signer
|
||||
{
|
||||
public:
|
||||
|
||||
EDDSA25519phSigner (const uint8_t * signingPrivateKey);
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
#if OPENSSL_EDDSA
|
||||
|
@ -530,6 +520,57 @@ namespace crypto
|
|||
RedDSA25519Signer signer (signingPrivateKey);
|
||||
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
}
|
||||
|
||||
#if OPENSSL_PQ
|
||||
#include <openssl/core_names.h>
|
||||
|
||||
// Post-Quantum
|
||||
const size_t MLDSA44_PUBLIC_KEY_LENGTH = 1312;
|
||||
const size_t MLDSA44_SIGNATURE_LENGTH = 2420;
|
||||
const size_t MLDSA44_PRIVATE_KEY_LENGTH = 2560;
|
||||
class MLDSA44Verifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
MLDSA44Verifier ();
|
||||
void SetPublicKey (const uint8_t * signingKey);
|
||||
~MLDSA44Verifier ();
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
||||
|
||||
size_t GetPublicKeyLen () const { return MLDSA44_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const { return MLDSA44_SIGNATURE_LENGTH; };
|
||||
size_t GetPrivateKeyLen () const { return MLDSA44_PRIVATE_KEY_LENGTH; };
|
||||
|
||||
private:
|
||||
|
||||
EVP_PKEY * m_Pkey;
|
||||
};
|
||||
|
||||
class MLDSA44Signer: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
MLDSA44Signer (const uint8_t * signingPrivateKey);
|
||||
~MLDSA44Signer ();
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
|
||||
private:
|
||||
|
||||
EVP_PKEY * m_Pkey;
|
||||
};
|
||||
|
||||
inline void CreateMLDSA44RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
EVP_PKEY * pkey = EVP_PKEY_Q_keygen (NULL, NULL, "ML-DSA-44");
|
||||
size_t len = MLDSA44_PUBLIC_KEY_LENGTH;
|
||||
EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PUB_KEY, signingPublicKey, MLDSA44_PUBLIC_KEY_LENGTH, &len);
|
||||
len = MLDSA44_PRIVATE_KEY_LENGTH;
|
||||
EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PRIV_KEY, signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH, &len);
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -19,39 +19,55 @@ namespace i2p
|
|||
{
|
||||
namespace stream
|
||||
{
|
||||
void SendBufferQueue::Add (std::shared_ptr<SendBuffer> buf)
|
||||
void SendBufferQueue::Add (std::shared_ptr<SendBuffer>&& buf)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
m_Buffers.push_back (buf);
|
||||
m_Size += buf->len;
|
||||
m_Buffers.push_back (std::move (buf));
|
||||
}
|
||||
}
|
||||
|
||||
size_t SendBufferQueue::Get (uint8_t * buf, size_t len)
|
||||
{
|
||||
if (!m_Size) return 0;
|
||||
size_t offset = 0;
|
||||
while (!m_Buffers.empty () && offset < len)
|
||||
if (len >= m_Size)
|
||||
{
|
||||
auto nextBuffer = m_Buffers.front ();
|
||||
auto rem = nextBuffer->GetRemainingSize ();
|
||||
if (offset + rem <= len)
|
||||
for (auto& it: m_Buffers)
|
||||
{
|
||||
// whole buffer
|
||||
memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem);
|
||||
auto rem = it->GetRemainingSize ();
|
||||
memcpy (buf + offset, it->GetRemaningBuffer (), rem);
|
||||
offset += rem;
|
||||
m_Buffers.pop_front (); // delete it
|
||||
}
|
||||
else
|
||||
m_Buffers.clear ();
|
||||
m_Size = 0;
|
||||
return offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!m_Buffers.empty () && offset < len)
|
||||
{
|
||||
// partially
|
||||
rem = len - offset;
|
||||
memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem);
|
||||
nextBuffer->offset += rem;
|
||||
offset = len; // break
|
||||
auto nextBuffer = m_Buffers.front ();
|
||||
auto rem = nextBuffer->GetRemainingSize ();
|
||||
if (offset + rem <= len)
|
||||
{
|
||||
// whole buffer
|
||||
memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem);
|
||||
offset += rem;
|
||||
m_Buffers.pop_front (); // delete it
|
||||
}
|
||||
else
|
||||
{
|
||||
// partially
|
||||
rem = len - offset;
|
||||
memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem);
|
||||
nextBuffer->offset += rem;
|
||||
offset = len; // break
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Size -= offset;
|
||||
m_Size -= offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -59,7 +75,7 @@ namespace stream
|
|||
{
|
||||
if (!m_Buffers.empty ())
|
||||
{
|
||||
for (auto it: m_Buffers)
|
||||
for (auto& it: m_Buffers)
|
||||
it->Cancel ();
|
||||
m_Buffers.clear ();
|
||||
m_Size = 0;
|
||||
|
@ -80,7 +96,7 @@ namespace stream
|
|||
m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO),
|
||||
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0),
|
||||
m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
|
@ -107,7 +123,7 @@ namespace stream
|
|||
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
|
||||
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
|
||||
m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
|
@ -613,10 +629,8 @@ namespace stream
|
|||
if (wasInitial)
|
||||
ScheduleResend ();
|
||||
}
|
||||
if (m_IsClientChoked && ackThrough > m_DropWindowDelaySequenceNumber)
|
||||
{
|
||||
if (m_IsClientChoked && ackThrough >= m_DropWindowDelaySequenceNumber)
|
||||
m_IsClientChoked = false;
|
||||
}
|
||||
if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber)
|
||||
{
|
||||
m_IsFirstRttSample = true;
|
||||
|
@ -719,10 +733,10 @@ namespace stream
|
|||
else if (handler)
|
||||
handler(boost::system::error_code ());
|
||||
auto s = shared_from_this ();
|
||||
boost::asio::post (m_Service, [s, buffer]()
|
||||
boost::asio::post (m_Service, [s, buffer = std::move(buffer)]() mutable
|
||||
{
|
||||
if (buffer)
|
||||
s->m_SendBuffer.Add (buffer);
|
||||
s->m_SendBuffer.Add (std::move(buffer));
|
||||
s->SendBuffer ();
|
||||
});
|
||||
}
|
||||
|
@ -1297,8 +1311,14 @@ namespace stream
|
|||
m_NumPacketsToSend = 1; m_PacingTimeRem = 0;
|
||||
}
|
||||
m_IsSendTime = true;
|
||||
if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
|
||||
if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime && m_RTT <= m_SlowRTT)
|
||||
{
|
||||
float winSize = m_WindowSize;
|
||||
if (m_WindowDropTargetSize)
|
||||
winSize = m_WindowDropTargetSize;
|
||||
float maxWinSize = MAX_WINDOW_SIZE;
|
||||
if (m_LastWindowIncTime)
|
||||
maxWinSize = (ts - m_LastWindowIncTime) / (m_RTT / MAX_WINDOW_SIZE_INC_PER_RTT) + winSize;
|
||||
for (int i = 0; i < m_NumPacketsToSend; i++)
|
||||
{
|
||||
if (m_WindowIncCounter)
|
||||
|
@ -1307,12 +1327,17 @@ namespace stream
|
|||
{
|
||||
if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize))
|
||||
m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here
|
||||
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize))
|
||||
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowDropTargetSize))
|
||||
m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here
|
||||
else
|
||||
m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize;
|
||||
if (m_WindowDropTargetSize > MAX_WINDOW_SIZE) m_WindowDropTargetSize = MAX_WINDOW_SIZE;
|
||||
m_WindowIncCounter--;
|
||||
if (m_WindowDropTargetSize >= maxWinSize)
|
||||
{
|
||||
m_WindowDropTargetSize = maxWinSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1324,11 +1349,17 @@ namespace stream
|
|||
m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize;
|
||||
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
|
||||
m_WindowIncCounter--;
|
||||
if (m_WindowSize >= maxWinSize)
|
||||
{
|
||||
m_WindowSize = maxWinSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
m_LastWindowIncTime = ts;
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
else if (m_WindowIncCounter && m_WindowSize == MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
|
||||
|
@ -1646,14 +1677,22 @@ namespace stream
|
|||
|
||||
void Stream::ProcessWindowDrop ()
|
||||
{
|
||||
if (m_WindowSize > m_LastWindowDropSize)
|
||||
{
|
||||
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
|
||||
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
|
||||
}
|
||||
if (m_WindowDropTargetSize)
|
||||
m_WindowDropTargetSize = (m_WindowDropTargetSize / 2) * 0.75; // congestion window size and -25% to drain queue
|
||||
else
|
||||
m_LastWindowDropSize = m_WindowSize;
|
||||
m_WindowDropTargetSize = m_LastWindowDropSize - (m_LastWindowDropSize / 4); // -25%;
|
||||
{
|
||||
if (m_WindowSize < m_LastWindowDropSize)
|
||||
{
|
||||
m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), (m_WindowSize - (m_LastWindowDropSize - m_WindowSize)));
|
||||
if (m_LastWindowDropSize < MIN_WINDOW_SIZE) m_LastWindowDropSize = MIN_WINDOW_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), ((m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2));
|
||||
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
|
||||
}
|
||||
m_WindowDropTargetSize = m_LastWindowDropSize * 0.75; // -25% to drain queue
|
||||
}
|
||||
if (m_WindowDropTargetSize < MIN_WINDOW_SIZE)
|
||||
m_WindowDropTargetSize = MIN_WINDOW_SIZE;
|
||||
m_WindowIncCounter = 0; // disable window growth
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -57,6 +57,7 @@ namespace stream
|
|||
const int INITIAL_WINDOW_SIZE = 10;
|
||||
const int MIN_WINDOW_SIZE = 3;
|
||||
const int MAX_WINDOW_SIZE = 512;
|
||||
const int MAX_WINDOW_SIZE_INC_PER_RTT = 16;
|
||||
const double RTT_EWMA_ALPHA = 0.25;
|
||||
const double SLOWRTT_EWMA_ALPHA = 0.05;
|
||||
const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer
|
||||
|
@ -149,7 +150,7 @@ namespace stream
|
|||
SendBufferQueue (): m_Size (0) {};
|
||||
~SendBufferQueue () { CleanUp (); };
|
||||
|
||||
void Add (std::shared_ptr<SendBuffer> buf);
|
||||
void Add (std::shared_ptr<SendBuffer>&& buf);
|
||||
size_t Get (uint8_t * buf, size_t len);
|
||||
size_t GetSize () const { return m_Size; };
|
||||
bool IsEmpty () const { return m_Buffers.empty (); };
|
||||
|
@ -299,7 +300,7 @@ namespace stream
|
|||
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_WindowSizeTail;
|
||||
double m_Jitter;
|
||||
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds
|
||||
m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime; // milliseconds
|
||||
m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime, m_LastWindowIncTime; // milliseconds
|
||||
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
|
||||
int m_NumResendAttempts, m_NumPacketsToSend;
|
||||
size_t m_MTU;
|
||||
|
|
|
@ -62,26 +62,22 @@ namespace data
|
|||
|
||||
std::string ToBase64 (size_t len = sz) const
|
||||
{
|
||||
char str[sz*2];
|
||||
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2);
|
||||
return std::string (str, str + l);
|
||||
return i2p::data::ByteStreamToBase64 (m_Buf, len);
|
||||
}
|
||||
|
||||
std::string ToBase32 (size_t len = sz) const
|
||||
{
|
||||
char str[sz*2];
|
||||
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2);
|
||||
return std::string (str, str + l);
|
||||
return i2p::data::ByteStreamToBase32 (m_Buf, len);
|
||||
}
|
||||
|
||||
size_t FromBase32 (std::string_view s)
|
||||
{
|
||||
return i2p::data::Base32ToByteStream (s.data (), s.length (), m_Buf, sz);
|
||||
return i2p::data::Base32ToByteStream (s, m_Buf, sz);
|
||||
}
|
||||
|
||||
size_t FromBase64 (std::string_view s)
|
||||
{
|
||||
return i2p::data::Base64ToByteStream (s.data (), s.length (), m_Buf, sz);
|
||||
return i2p::data::Base64ToByteStream (s, m_Buf, sz);
|
||||
}
|
||||
|
||||
uint8_t GetBit (int i) const
|
||||
|
|
|
@ -131,27 +131,35 @@ namespace tunnel
|
|||
|
||||
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
|
||||
std::lock_guard<std::mutex> l(m_HandleMutex);
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
||||
if (!m_Endpoint) m_Endpoint = std::make_unique<TunnelEndpoint>(false); // transit endpoint is always outbound
|
||||
m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg);
|
||||
}
|
||||
|
||||
void TransitTunnelEndpoint::FlushTunnelDataMsgs ()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_HandleMutex);
|
||||
m_Endpoint.FlushI2NPMsgs ();
|
||||
if (m_Endpoint)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_HandleMutex);
|
||||
m_Endpoint->FlushI2NPMsgs ();
|
||||
}
|
||||
}
|
||||
|
||||
void TransitTunnelEndpoint::Cleanup ()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_HandleMutex);
|
||||
m_Endpoint.Cleanup ();
|
||||
if (m_Endpoint)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_HandleMutex);
|
||||
m_Endpoint->Cleanup ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string TransitTunnelEndpoint::GetNextPeerName () const
|
||||
{
|
||||
auto hash = m_Endpoint.GetCurrentHash ();
|
||||
if (!m_Endpoint) return "";
|
||||
auto hash = m_Endpoint->GetCurrentHash ();
|
||||
if (hash)
|
||||
{
|
||||
const auto& sender = m_Endpoint.GetSender ();
|
||||
const auto& sender = m_Endpoint->GetSender ();
|
||||
if (sender)
|
||||
{
|
||||
auto transport = sender->GetCurrentTransport ();
|
||||
|
|
|
@ -97,20 +97,19 @@ namespace tunnel
|
|||
TransitTunnelEndpoint (uint32_t receiveTunnelID,
|
||||
const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
|
||||
const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
|
||||
m_Endpoint (false) {}; // transit endpoint is always outbound
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
|
||||
|
||||
void Cleanup () override;
|
||||
|
||||
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) override;
|
||||
void FlushTunnelDataMsgs () override;
|
||||
size_t GetNumTransmittedBytes () const override { return m_Endpoint.GetNumReceivedBytes (); }
|
||||
size_t GetNumTransmittedBytes () const override { return m_Endpoint ? m_Endpoint->GetNumReceivedBytes () : 0; }
|
||||
std::string GetNextPeerName () const override;
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_HandleMutex;
|
||||
TunnelEndpoint m_Endpoint;
|
||||
std::unique_ptr<TunnelEndpoint> m_Endpoint;
|
||||
};
|
||||
|
||||
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -10,7 +10,7 @@
|
|||
#define TRANSPORT_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
@ -28,45 +28,51 @@ namespace transport
|
|||
const size_t IPV6_HEADER_SIZE = 40;
|
||||
const size_t UDP_HEADER_SIZE = 8;
|
||||
|
||||
template<size_t sz>
|
||||
class SignedData
|
||||
{
|
||||
public:
|
||||
|
||||
SignedData () {}
|
||||
SignedData (): m_Size(0) {}
|
||||
SignedData (const SignedData& other)
|
||||
{
|
||||
m_Stream << other.m_Stream.rdbuf ();
|
||||
m_Size = other.m_Size;
|
||||
memcpy (m_Buf, other.m_Buf, m_Size);
|
||||
}
|
||||
|
||||
void Reset ()
|
||||
{
|
||||
m_Stream.str("");
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
void Insert (const uint8_t * buf, size_t len)
|
||||
size_t Insert (const uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Stream.write ((char *)buf, len);
|
||||
if (m_Size + len > sz) len = sz - m_Size;
|
||||
memcpy (m_Buf + m_Size, buf, len);
|
||||
m_Size += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Insert (T t)
|
||||
{
|
||||
m_Stream.write ((char *)&t, sizeof (T));
|
||||
Insert ((const uint8_t *)&t, sizeof (T));
|
||||
}
|
||||
|
||||
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
|
||||
{
|
||||
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
return ident->Verify (m_Buf, m_Size, signature);
|
||||
}
|
||||
|
||||
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
|
||||
{
|
||||
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
keys.Sign (m_Buf, m_Size, signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::stringstream m_Stream;
|
||||
uint8_t m_Buf[sz];
|
||||
size_t m_Size;
|
||||
};
|
||||
|
||||
const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds
|
||||
|
|
|
@ -680,8 +680,21 @@ namespace transport
|
|||
auto directTransports = compatibleTransports & peer->router->GetPublishedTransports ();
|
||||
peer->numAttempts = 0;
|
||||
peer->priority.clear ();
|
||||
bool isReal = peer->router->GetProfile ()->IsReal ();
|
||||
bool ssu2 = isReal ? (m_Rng () & 1) : false; // try NTCP2 if router is not confirmed real
|
||||
|
||||
std::shared_ptr<RouterProfile> profile;
|
||||
if (peer->router->HasProfile ()) profile = peer->router->GetProfile (); // only if in memory
|
||||
bool ssu2 = false; // NTCP2 by default
|
||||
bool isReal = profile ? profile->IsReal () : true;
|
||||
if (isReal)
|
||||
{
|
||||
ssu2 = m_Rng () & 1; // 1/2
|
||||
if (ssu2 && !profile)
|
||||
{
|
||||
profile = peer->router->GetProfile (); // load profile if necessary
|
||||
isReal = profile->IsReal ();
|
||||
if (!isReal) ssu2 = false; // try NTCP2 if router is not confirmed real
|
||||
}
|
||||
}
|
||||
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
|
||||
if (directTransports)
|
||||
{
|
||||
|
@ -1182,7 +1195,7 @@ namespace transport
|
|||
std::lock_guard<std::mutex> lock(m_TrustedRoutersMutex);
|
||||
m_TrustedRouters.clear();
|
||||
for (const auto & ri : routers )
|
||||
m_TrustedRouters.push_back(ri);
|
||||
m_TrustedRouters.insert(ri);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const
|
||||
|
@ -1223,23 +1236,32 @@ namespace transport
|
|||
auto sz = m_TrustedRouters.size();
|
||||
if (sz)
|
||||
{
|
||||
if(sz == 1)
|
||||
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
|
||||
auto it = m_TrustedRouters.begin();
|
||||
std::advance(it, m_Rng() % sz);
|
||||
if(sz > 1)
|
||||
std::advance(it, m_Rng() % sz);
|
||||
return i2p::data::netdb.FindRouter(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
|
||||
bool Transports::IsTrustedRouter (const i2p::data::IdentHash& ih) const
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_TrustedRoutersMutex);
|
||||
for (const auto & r : m_TrustedRouters )
|
||||
if ( r == ih ) return true;
|
||||
}
|
||||
if (m_TrustedRouters.empty ()) return false;
|
||||
std::lock_guard<std::mutex> l(m_TrustedRoutersMutex);
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (m_TrustedRouters.contains (ih))
|
||||
#else
|
||||
if (m_TrustedRouters.count (ih) > 0)
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash& ih) const
|
||||
{
|
||||
if (IsTrustedRouter (ih)) return true;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
||||
auto ri = i2p::data::netdb.FindRouter(ih);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
@ -178,7 +179,8 @@ namespace transport
|
|||
/** restrict routes to use only these routers for first hops */
|
||||
void RestrictRoutesToRouters(const std::set<i2p::data::IdentHash>& routers);
|
||||
|
||||
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
|
||||
bool IsTrustedRouter (const i2p::data::IdentHash& ih) const;
|
||||
bool IsRestrictedPeer(const i2p::data::IdentHash& ih) const;
|
||||
|
||||
void PeerTest (bool ipv4 = true, bool ipv6 = true);
|
||||
|
||||
|
@ -237,7 +239,7 @@ namespace transport
|
|||
mutable std::mutex m_FamilyMutex;
|
||||
|
||||
/** which routers for first hop to trust */
|
||||
std::vector<i2p::data::IdentHash> m_TrustedRouters;
|
||||
std::unordered_set<i2p::data::IdentHash> m_TrustedRouters;
|
||||
mutable std::mutex m_TrustedRoutersMutex;
|
||||
|
||||
i2p::I2NPMessagesHandler m_LoopbackHandler;
|
||||
|
|
|
@ -351,10 +351,13 @@ namespace tunnel
|
|||
{
|
||||
it.second.first->SetState (eTunnelStateFailed);
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
if (m_OutboundTunnels.size () > 1 || m_NumOutboundTunnels <= 1) // don't fail last tunnel
|
||||
if (m_OutboundTunnels.size () > 1) // don't fail last tunnel
|
||||
m_OutboundTunnels.erase (it.second.first);
|
||||
else
|
||||
{
|
||||
it.second.first->SetState (eTunnelStateTestFailed);
|
||||
CreateOutboundTunnel (); // create new tunnel immediately because last one failed
|
||||
}
|
||||
}
|
||||
else if (it.second.first->GetState () != eTunnelStateExpiring)
|
||||
it.second.first->SetState (eTunnelStateTestFailed);
|
||||
|
@ -368,13 +371,16 @@ namespace tunnel
|
|||
bool failed = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel
|
||||
if (m_InboundTunnels.size () > 1) // don't fail last tunnel
|
||||
{
|
||||
m_InboundTunnels.erase (it.second.second);
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
it.second.second->SetState (eTunnelStateTestFailed);
|
||||
CreateInboundTunnel (); // create new tunnel immediately because last one failed
|
||||
}
|
||||
}
|
||||
if (failed && m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated (true);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <unordered_set>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
|
@ -123,8 +124,8 @@ const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#define address_pair_v4(a,b) { boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () }
|
||||
#define address_pair_v6(a,b) { boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () }
|
||||
#define address_pair_v4(a,b) std::pair{ boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () }
|
||||
#define address_pair_v6(a,b) std::pair{ boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () }
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -647,7 +648,8 @@ namespace net
|
|||
if (host.is_unspecified ()) return false;
|
||||
if (host.is_v4())
|
||||
{
|
||||
static const std::vector< std::pair<uint32_t, uint32_t> > reservedIPv4Ranges {
|
||||
static const std::array<std::pair<uint32_t, uint32_t>, 14> reservedIPv4Ranges
|
||||
{
|
||||
address_pair_v4("0.0.0.0", "0.255.255.255"),
|
||||
address_pair_v4("10.0.0.0", "10.255.255.255"),
|
||||
address_pair_v4("100.64.0.0", "100.127.255.255"),
|
||||
|
@ -672,7 +674,8 @@ namespace net
|
|||
}
|
||||
if (host.is_v6())
|
||||
{
|
||||
static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges {
|
||||
static const std::array<std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type>, 7> reservedIPv6Ranges
|
||||
{
|
||||
address_pair_v6("64:ff9b::", "64:ff9b:ffff:ffff:ffff:ffff:ffff:ffff"), // NAT64
|
||||
address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
|
||||
address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
|
||||
|
|
|
@ -443,17 +443,18 @@ namespace client
|
|||
auto addr = std::make_shared<const Address>(address.substr (0, pos));
|
||||
return addr->IsValid () ? addr : nullptr;
|
||||
}
|
||||
else
|
||||
else
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (address.ends_with (".i2p"))
|
||||
#else
|
||||
if (address.find (".i2p") != std::string::npos)
|
||||
#endif
|
||||
{
|
||||
pos = address.find (".i2p");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (!m_IsEnabled) return nullptr;
|
||||
auto addr = FindAddress (address);
|
||||
if (!addr)
|
||||
LookupAddress (address); // TODO:
|
||||
return addr;
|
||||
}
|
||||
if (!m_IsEnabled) return nullptr;
|
||||
auto addr = FindAddress (address);
|
||||
if (!addr)
|
||||
LookupAddress (address); // TODO:
|
||||
return addr;
|
||||
}
|
||||
// if not .b32 we assume full base64 address
|
||||
i2p::data::IdentityEx dest;
|
||||
|
@ -566,29 +567,35 @@ namespace client
|
|||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string name = s.substr(0, pos++);
|
||||
std::string addr = s.substr(pos);
|
||||
std::string_view name = std::string_view(s).substr(0, pos++);
|
||||
std::string_view addr = std::string_view(s).substr(pos);
|
||||
|
||||
size_t pos = addr.find('#');
|
||||
if (pos != std::string::npos)
|
||||
if (pos != addr.npos)
|
||||
addr = addr.substr(0, pos); // remove comments
|
||||
|
||||
pos = name.find(".b32.i2p");
|
||||
if (pos != std::string::npos)
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (name.ends_with (".b32.i2p"))
|
||||
#else
|
||||
if (name.find(".b32.i2p") != name.npos)
|
||||
#endif
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Skipped adding of b32 address: ", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos = name.find(".i2p");
|
||||
if (pos == std::string::npos)
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (!name.ends_with (".i2p"))
|
||||
#else
|
||||
if (name.find(".i2p") == name.npos)
|
||||
#endif
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Malformed domain: ", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ident = std::make_shared<i2p::data::IdentityEx> ();
|
||||
if (!ident->FromBase64(addr)) {
|
||||
if (!ident->FromBase64(addr))
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Malformed address ", addr, " for ", name);
|
||||
incomplete = f.eof ();
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -16,6 +16,24 @@ namespace i2p
|
|||
{
|
||||
namespace client
|
||||
{
|
||||
void BOBI2PTunnelIncomingConnection::Established ()
|
||||
{
|
||||
if (m_IsQuiet)
|
||||
StreamReceive ();
|
||||
else
|
||||
{
|
||||
// send destination first like received from I2P
|
||||
std::string dest = GetStream ()->GetRemoteIdentity ()->ToBase64 ();
|
||||
dest += "\n";
|
||||
if (dest.size() <= I2P_TUNNEL_CONNECTION_BUFFER_SIZE)
|
||||
memcpy (GetStreamBuffer (), dest.c_str (), dest.size ());
|
||||
else
|
||||
memset (GetStreamBuffer (), 0, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
|
||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
||||
}
|
||||
Receive ();
|
||||
}
|
||||
|
||||
BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<ClientDestination> localDestination):
|
||||
BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep)
|
||||
{
|
||||
|
@ -156,7 +174,7 @@ namespace client
|
|||
{
|
||||
if (stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, m_Endpoint, m_IsQuiet);
|
||||
auto conn = std::make_shared<BOBI2PTunnelIncomingConnection> (this, stream, m_Endpoint, m_IsQuiet);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -71,6 +71,23 @@ namespace client
|
|||
const char BOB_HELP_STATUS[] = "status <NICKNAME> - Display status of a nicknamed tunnel.";
|
||||
const char BOB_HELP_HELP [] = "help <COMMAND> - Get help on a command.";
|
||||
|
||||
class BOBI2PTunnelIncomingConnection: public I2PTunnelConnection
|
||||
{
|
||||
public:
|
||||
|
||||
BOBI2PTunnelIncomingConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
||||
I2PTunnelConnection (owner, stream, target), m_IsQuiet (quiet) {};
|
||||
|
||||
protected:
|
||||
|
||||
void Established () override;
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsQuiet; // don't send destination
|
||||
};
|
||||
|
||||
class BOBI2PTunnel: public I2PService
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -265,11 +265,15 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
|
||||
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename,
|
||||
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType)
|
||||
{
|
||||
static const std::string transient("transient");
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (filename.starts_with ("transient"))
|
||||
#else
|
||||
std::string_view transient("transient");
|
||||
if (!filename.compare (0, transient.length (), transient)) // starts with transient
|
||||
#endif
|
||||
{
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
|
||||
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
|
||||
|
@ -297,7 +301,7 @@ namespace client
|
|||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogCritical, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
|
||||
LogPrint (eLogInfo, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
size_t len = keys.GetFullLen ();
|
||||
|
@ -543,7 +547,11 @@ namespace client
|
|||
{
|
||||
for (auto& it: files)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
if (!it.ends_with (".conf")) continue;
|
||||
#else
|
||||
if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf"
|
||||
#endif
|
||||
LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it);
|
||||
ReadTunnels (it, numClientTunnels, numServerTunnels);
|
||||
}
|
||||
|
@ -599,7 +607,9 @@ namespace client
|
|||
options[I2CP_PARAM_OUTBOUND_NICKNAME] = name;
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
if (keys.length () > 0)
|
||||
if (keys == "shareddest")
|
||||
localDestination = m_SharedLocalDestination;
|
||||
else if (keys.length () > 0)
|
||||
{
|
||||
auto it = destinations.find (keys);
|
||||
if (it != destinations.end ())
|
||||
|
@ -758,26 +768,31 @@ namespace client
|
|||
options[I2CP_PARAM_INBOUND_NICKNAME] = name;
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
auto it = destinations.find (keys);
|
||||
if (it != destinations.end ())
|
||||
{
|
||||
localDestination = it->second;
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
if (keys == "shareddest")
|
||||
localDestination = m_SharedLocalDestination;
|
||||
else
|
||||
{
|
||||
i2p::data::PrivateKeys k;
|
||||
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))
|
||||
continue;
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
{
|
||||
auto it = destinations.find (keys);
|
||||
if (it != destinations.end ())
|
||||
{
|
||||
localDestination = CreateNewLocalDestination (k, true, &options);
|
||||
destinations[keys] = localDestination;
|
||||
localDestination = it->second;
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
else
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
{
|
||||
i2p::data::PrivateKeys k;
|
||||
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))
|
||||
continue;
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
{
|
||||
localDestination = CreateNewLocalDestination (k, true, &options);
|
||||
destinations[keys] = localDestination;
|
||||
}
|
||||
else
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
}
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
||||
{
|
||||
// udp server tunnel
|
||||
|
@ -871,7 +886,7 @@ namespace client
|
|||
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf);
|
||||
LogPrint (eLogError, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
|
@ -897,7 +912,12 @@ namespace client
|
|||
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
|
||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
||||
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
||||
if (httpProxyKeys.length () > 0)
|
||||
if (httpProxyKeys == "shareddest")
|
||||
{
|
||||
localDestination = m_SharedLocalDestination;
|
||||
localDestination->Acquire ();
|
||||
}
|
||||
else if (httpProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
if(LoadPrivateKeys (keys, httpProxyKeys, sigType))
|
||||
|
@ -941,7 +961,12 @@ namespace client
|
|||
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
|
||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType);
|
||||
LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
|
||||
if (httpProxyKeys == socksProxyKeys && m_HttpProxy)
|
||||
if (socksProxyKeys == "shareddest")
|
||||
{
|
||||
localDestination = m_SharedLocalDestination;
|
||||
localDestination->Acquire ();
|
||||
}
|
||||
else if (httpProxyKeys == socksProxyKeys && m_HttpProxy)
|
||||
{
|
||||
localDestination = m_HttpProxy->GetLocalDestination ();
|
||||
localDestination->Acquire ();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -92,7 +92,7 @@ namespace client
|
|||
const std::string & name, const std::map<std::string, std::string> * params = nullptr);
|
||||
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
||||
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
||||
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
|
||||
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename,
|
||||
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
|
||||
|
|
|
@ -29,13 +29,15 @@ namespace client
|
|||
const std::map<std::string, std::string>& params):
|
||||
LeaseSetDestination (service, isPublic, ¶ms),
|
||||
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()),
|
||||
m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), m_LeaseSetCreationTimer (service)
|
||||
m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread),
|
||||
m_LeaseSetCreationTimer (service), m_ReadinessCheckTimer (service)
|
||||
{
|
||||
}
|
||||
|
||||
void I2CPDestination::Stop ()
|
||||
{
|
||||
m_LeaseSetCreationTimer.cancel ();
|
||||
m_ReadinessCheckTimer.cancel ();
|
||||
LeaseSetDestination::Stop ();
|
||||
m_Owner = nullptr;
|
||||
}
|
||||
|
@ -77,7 +79,13 @@ namespace client
|
|||
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType;
|
||||
}
|
||||
|
||||
|
||||
i2p::data::CryptoKeyType I2CPDestination::GetPreferredCryptoType () const
|
||||
{
|
||||
if (m_ECIESx25519Decryptor)
|
||||
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
|
||||
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
|
||||
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
|
@ -88,7 +96,7 @@ namespace client
|
|||
|
||||
void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
{
|
||||
boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
|
||||
boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, GetSharedFromThis (), tunnels));
|
||||
}
|
||||
|
||||
void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
|
@ -98,6 +106,20 @@ namespace client
|
|||
LogPrint (eLogInfo, "I2CP: LeaseSet is being created");
|
||||
return;
|
||||
}
|
||||
m_ReadinessCheckTimer.cancel ();
|
||||
auto pool = GetTunnelPool ();
|
||||
if (!pool || pool->GetOutboundTunnels ().empty ())
|
||||
{
|
||||
// try again later
|
||||
m_ReadinessCheckTimer.expires_from_now (boost::posix_time::seconds(I2CP_DESTINATION_READINESS_CHECK_INTERVAL));
|
||||
m_ReadinessCheckTimer.async_wait(
|
||||
[s=GetSharedFromThis (), tunnels=std::move(tunnels)](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
s->PostCreateNewLeaseSet (tunnels);
|
||||
});
|
||||
return;
|
||||
}
|
||||
uint8_t priv[256] = {0};
|
||||
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
|
||||
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
|
||||
|
@ -511,7 +533,7 @@ namespace client
|
|||
if (sendBuf)
|
||||
{
|
||||
if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
|
||||
m_SendQueue.Add (sendBuf);
|
||||
m_SendQueue.Add (std::move(sendBuf));
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
|
||||
|
@ -555,7 +577,7 @@ namespace client
|
|||
m_IsSending = false;
|
||||
}
|
||||
|
||||
std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len)
|
||||
std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
uint8_t l = buf[0];
|
||||
if (l > len) l = len;
|
||||
|
@ -572,7 +594,7 @@ namespace client
|
|||
return l + 1;
|
||||
}
|
||||
|
||||
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping)
|
||||
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const
|
||||
// TODO: move to Base.cpp
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
@ -1057,7 +1079,7 @@ namespace client
|
|||
if (sendBuf)
|
||||
{
|
||||
if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
|
||||
m_SendQueue.Add (sendBuf);
|
||||
m_SendQueue.Add (std::move(sendBuf));
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "util.h"
|
||||
#include "Destination.h"
|
||||
#include "Streaming.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -31,6 +32,7 @@ namespace client
|
|||
const size_t I2CP_MAX_MESSAGE_LENGTH = 65535;
|
||||
const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M
|
||||
const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds
|
||||
const int I2CP_DESTINATION_READINESS_CHECK_INTERVAL = 5; // in seconds
|
||||
const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds
|
||||
|
||||
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
|
||||
|
@ -88,7 +90,7 @@ namespace client
|
|||
const std::map<std::string, std::string>& params);
|
||||
~I2CPDestination () {};
|
||||
|
||||
void Stop ();
|
||||
void Stop () override;
|
||||
|
||||
void SetEncryptionPrivateKey (const uint8_t * key);
|
||||
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
|
||||
|
@ -99,18 +101,25 @@ namespace client
|
|||
bool SendMsg (const uint8_t * payload, size_t len, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession, uint32_t nonce);
|
||||
|
||||
// implements LocalDestination
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override; // for 4 only
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override { return m_Identity; };
|
||||
|
||||
protected:
|
||||
|
||||
void CleanupDestination ();
|
||||
// GarlicDestination
|
||||
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override
|
||||
{
|
||||
return m_ECIESx25519Decryptor ? i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD : 0;
|
||||
}
|
||||
// LeaseSetDestination
|
||||
void CleanupDestination () override;
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const override;
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len) override;
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
|
||||
|
@ -131,7 +140,7 @@ namespace client
|
|||
uint8_t m_ECIESx25519PrivateKey[32];
|
||||
uint64_t m_LeaseSetExpirationTime;
|
||||
bool m_IsCreatingLeaseSet, m_IsSameThread;
|
||||
boost::asio::deadline_timer m_LeaseSetCreationTimer;
|
||||
boost::asio::deadline_timer m_LeaseSetCreationTimer, m_ReadinessCheckTimer;
|
||||
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
|
||||
};
|
||||
|
||||
|
@ -193,9 +202,9 @@ namespace client
|
|||
|
||||
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
std::string_view ExtractString (const uint8_t * buf, size_t len);
|
||||
std::string_view ExtractString (const uint8_t * buf, size_t len) const;
|
||||
size_t PutString (uint8_t * buf, size_t len, std::string_view str);
|
||||
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
|
||||
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const;
|
||||
void SendSessionStatusMessage (I2CPSessionStatus status);
|
||||
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -32,8 +32,7 @@ namespace client
|
|||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint16_t port):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
|
||||
m_IsQuiet (true)
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ())
|
||||
{
|
||||
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
|
||||
}
|
||||
|
@ -41,14 +40,13 @@ namespace client
|
|||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
||||
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
|
||||
m_RemoteEndpoint (socket->remote_endpoint ())
|
||||
{
|
||||
}
|
||||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
||||
const boost::asio::ip::tcp::endpoint& target,std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target)
|
||||
{
|
||||
m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ());
|
||||
if (sslCtx)
|
||||
|
@ -292,18 +290,7 @@ namespace client
|
|||
|
||||
void I2PTunnelConnection::Established ()
|
||||
{
|
||||
if (m_IsQuiet)
|
||||
StreamReceive ();
|
||||
else
|
||||
{
|
||||
// send destination first like received from I2P
|
||||
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
|
||||
dest += "\n";
|
||||
if(sizeof(m_StreamBuffer) >= dest.size()) {
|
||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
||||
}
|
||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
||||
}
|
||||
StreamReceive ();
|
||||
Receive ();
|
||||
}
|
||||
|
||||
|
@ -377,7 +364,7 @@ namespace client
|
|||
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), m_XI2P (XI2P),
|
||||
I2PTunnelConnection (owner, stream, target, sslCtx), m_Host (host), m_XI2P (XI2P),
|
||||
m_HeaderSent (false), m_ResponseHeaderSent (false)
|
||||
{
|
||||
if (sslCtx)
|
||||
|
@ -404,7 +391,7 @@ namespace client
|
|||
else
|
||||
{
|
||||
// strip up some headers
|
||||
static const std::vector<std::string> excluded // list of excluded headers
|
||||
static const std::array<std::string_view, 2> excluded // list of excluded headers
|
||||
{
|
||||
"Keep-Alive:", "X-I2P"
|
||||
};
|
||||
|
@ -487,7 +474,7 @@ namespace client
|
|||
if (line == "\r") endOfHeader = true;
|
||||
else
|
||||
{
|
||||
static const std::vector<std::string> excluded // list of excluded headers
|
||||
static const std::array<std::string_view, 5> excluded // list of excluded headers
|
||||
{
|
||||
"Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy"
|
||||
};
|
||||
|
@ -528,7 +515,7 @@ namespace client
|
|||
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()),
|
||||
I2PTunnelConnection (owner, stream, target, sslCtx), m_From (stream->GetRemoteIdentity ()),
|
||||
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
|
||||
{
|
||||
}
|
||||
|
@ -857,7 +844,7 @@ namespace client
|
|||
|
||||
std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), true, m_SSLCtx);
|
||||
return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), m_SSLCtx);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -27,7 +27,7 @@ namespace i2p
|
|||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536;
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 16384;
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
// for HTTP tunnels
|
||||
|
@ -45,7 +45,7 @@ namespace client
|
|||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true,
|
||||
const boost::asio::ip::tcp::endpoint& target,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P
|
||||
~I2PTunnelConnection ();
|
||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
||||
|
@ -54,25 +54,27 @@ namespace client
|
|||
|
||||
protected:
|
||||
|
||||
virtual void Established ();
|
||||
void Terminate ();
|
||||
|
||||
void Receive ();
|
||||
void StreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
|
||||
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
|
||||
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
|
||||
std::shared_ptr<i2p::stream::Stream> GetStream () const { return m_Stream; };
|
||||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; };
|
||||
|
||||
uint8_t * GetStreamBuffer () { return m_StreamBuffer; };
|
||||
|
||||
private:
|
||||
|
||||
void HandleConnect (const boost::system::error_code& ecode);
|
||||
void HandleHandshake (const boost::system::error_code& ecode);
|
||||
void Established ();
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
|
||||
|
@ -80,7 +82,6 @@ namespace client
|
|||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
||||
bool m_IsQuiet; // don't send destination
|
||||
};
|
||||
|
||||
class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection
|
||||
|
@ -94,7 +95,7 @@ namespace client
|
|||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
void Write (const uint8_t * buf, size_t len) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -112,8 +113,8 @@ namespace client
|
|||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
void WriteToStream (const uint8_t * buf, size_t len);
|
||||
void Write (const uint8_t * buf, size_t len) override;
|
||||
void WriteToStream (const uint8_t * buf, size_t len) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -132,7 +133,7 @@ namespace client
|
|||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
void Write (const uint8_t * buf, size_t len) override;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -415,7 +415,7 @@ namespace client
|
|||
{
|
||||
session->UDPEndpoint = forward;
|
||||
auto dest = session->GetLocalDestination ()->CreateDatagramDestination ();
|
||||
auto port = std::stoi(params[SAM_PARAM_PORT]);
|
||||
auto port = forward ? std::stoi(params[SAM_PARAM_PORT]) : 0;
|
||||
if (type == eSAMSessionTypeDatagram)
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
|
||||
|
@ -470,15 +470,11 @@ namespace client
|
|||
auto session = m_Owner.FindSession(m_ID);
|
||||
if (session)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char priv[1024];
|
||||
size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
||||
priv[l1] = 0;
|
||||
std::string priv = session->GetLocalDestination ()->GetPrivateKeys ().ToBase64 ();
|
||||
#ifdef _MSC_VER
|
||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ());
|
||||
#else
|
||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, l2, false);
|
||||
}
|
||||
|
@ -1116,18 +1112,19 @@ namespace client
|
|||
}
|
||||
}
|
||||
if (!m_IsSilent)
|
||||
{
|
||||
// get remote peer address
|
||||
auto ident_ptr = stream->GetRemoteIdentity();
|
||||
const size_t ident_len = ident_ptr->GetFullLen();
|
||||
uint8_t* ident = new uint8_t[ident_len];
|
||||
|
||||
// send remote peer address as base64
|
||||
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
|
||||
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
delete[] ident;
|
||||
m_StreamBuffer[l1] = '\n';
|
||||
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
|
||||
{
|
||||
if (m_SocketType != eSAMSocketTypeTerminated)
|
||||
{
|
||||
// get remote peer address
|
||||
auto ident = std::make_shared<std::string>(stream->GetRemoteIdentity()->ToBase64 ()); // we need to keep it until sent
|
||||
ident->push_back ('\n');
|
||||
// send remote peer address back to client like received from stream
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (ident->data (), ident->size ()), boost::asio::transfer_all(),
|
||||
[ident, s = shared_from_this ()](const boost::system::error_code& ecode, size_t bytes_transferred)
|
||||
{
|
||||
s->HandleWriteI2PData (ecode, bytes_transferred);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
I2PReceive ();
|
||||
|
|
Loading…
Add table
Reference in a new issue