aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/websocketpp_client.cpp191
-rw-r--r--src/data/event.cpp (renamed from src/event.cpp)29
-rw-r--r--src/data/filters.cpp (renamed from src/filters.cpp)21
-rw-r--r--src/service/nostr_service_base.cpp (renamed from src/nostr_service.cpp)300
-rw-r--r--src/signer/noscrypt_signer.cpp697
5 files changed, 963 insertions, 275 deletions
diff --git a/src/client/websocketpp_client.cpp b/src/client/websocketpp_client.cpp
index c7504e1..b8458e6 100644
--- a/src/client/websocketpp_client.cpp
+++ b/src/client/websocketpp_client.cpp
@@ -1,129 +1,108 @@
-#include "web_socket_client.hpp"
-
-using std::error_code;
-using std::function;
-using std::lock_guard;
-using std::make_tuple;
-using std::mutex;
-using std::string;
-using std::tuple;
-using std::unordered_map;
-
-namespace client
-{
-/**
- * @brief An implementation of the `IWebSocketClient` interface that uses the WebSocket++ library.
- */
-class WebsocketppClient : public IWebSocketClient
-{
-public:
- void start() override
- {
- this->_client.init_asio();
- this->_client.start_perpetual();
- };
+#include <mutex>
- void stop() override
- {
- this->_client.stop_perpetual();
- this->_client.stop();
- };
+#include "client/websocketpp_client.hpp"
- void openConnection(string uri) override
- {
- error_code error;
- websocketpp_client::connection_ptr connection = this->_client.get_connection(uri, error);
+using namespace nostr::client;
+using namespace std;
- if (error.value() == -1)
- {
- // PLOG_ERROR << "Error connecting to relay " << relay << ": " << error.message();
- }
-
- // Configure the connection here via the connection pointer.
- connection->set_fail_handler([this, uri](auto handle) {
- // PLOG_ERROR << "Error connecting to relay " << relay << ": Handshake failed.";
- lock_guard<mutex> lock(this->_propertyMutex);
- if (this->isConnected(uri))
- {
- this->_connectionHandles.erase(uri);
- }
- });
+void WebsocketppClient::start()
+{
+ this->_client.init_asio();
+ this->_client.start_perpetual();
+};
- lock_guard<mutex> lock(this->_propertyMutex);
- this->_connectionHandles[uri] = connection->get_handle();
- this->_client.connect(connection);
- };
+void WebsocketppClient::stop()
+{
+ this->_client.stop_perpetual();
+ this->_client.stop();
+};
- bool isConnected(string uri) override
- {
- lock_guard<mutex> lock(this->_propertyMutex);
- return this->_connectionHandles.find(uri) != this->_connectionHandles.end();
- };
+void WebsocketppClient::openConnection(string uri)
+{
+ error_code error;
+ websocketpp_client::connection_ptr connection = this->_client.get_connection(uri, error);
- tuple<string, bool> send(string message, string uri) override
+ if (error.value() == -1)
{
- error_code error;
+ // PLOG_ERROR << "Error connecting to relay " << relay << ": " << error.message();
+ }
- // Make sure the connection isn't closed from under us.
+ // Configure the connection here via the connection pointer.
+ connection->set_fail_handler([this, uri](auto handle) {
+ // PLOG_ERROR << "Error connecting to relay " << relay << ": Handshake failed.";
lock_guard<mutex> lock(this->_propertyMutex);
- this->_client.send(
- this->_connectionHandles[uri],
- message,
- websocketpp::frame::opcode::text,
- error);
-
- if (error.value() == -1)
+ if (this->isConnected(uri))
{
- return make_tuple(uri, false);
+ this->_connectionHandles.erase(uri);
}
+ });
- return make_tuple(uri, true);
- };
+ lock_guard<mutex> lock(this->_propertyMutex);
+ this->_connectionHandles[uri] = connection->get_handle();
+ this->_client.connect(connection);
+};
- tuple<string, bool> send(string message, string uri, function<void(const string&)> messageHandler) override
- {
- auto successes = this->send(message, uri);
- this->receive(uri, messageHandler);
- return successes;
- };
+bool WebsocketppClient::isConnected(string uri)
+{
+ lock_guard<mutex> lock(this->_propertyMutex);
+ return this->_connectionHandles.find(uri) != this->_connectionHandles.end();
+};
- void receive(string uri, function<void(const string&)> messageHandler) override
- {
- lock_guard<mutex> lock(this->_propertyMutex);
- auto connectionHandle = this->_connectionHandles[uri];
- auto connection = this->_client.get_con_from_hdl(connectionHandle);
+tuple<string, bool> WebsocketppClient::send(string message, string uri)
+{
+ error_code error;
- connection->set_message_handler([messageHandler](
- websocketpp::connection_hdl connectionHandle,
- websocketpp_client::message_ptr message)
- {
- messageHandler(message->get_payload());
- });
- };
+ // Make sure the connection isn't closed from under us.
+ lock_guard<mutex> lock(this->_propertyMutex);
+ this->_client.send(
+ this->_connectionHandles[uri],
+ message,
+ websocketpp::frame::opcode::text,
+ error);
- void closeConnection(string uri) override
+ if (error.value() == -1)
{
- lock_guard<mutex> lock(this->_propertyMutex);
+ return make_tuple(uri, false);
+ }
- websocketpp::connection_hdl handle = this->_connectionHandles[uri];
- this->_client.close(
- handle,
- websocketpp::close::status::going_away,
- "_client requested close.");
-
- this->_connectionHandles.erase(uri);
- };
+ return make_tuple(uri, true);
+};
-private:
- typedef websocketpp::client<websocketpp::config::asio_client> websocketpp_client;
- typedef unordered_map<string, websocketpp::connection_hdl>::iterator connection_hdl_iterator;
+tuple<string, bool> WebsocketppClient::send(
+ string message,
+ string uri,
+ function<void(const string&)> messageHandler)
+{
+ auto successes = this->send(message, uri);
+ this->receive(uri, messageHandler);
+ return successes;
+};
- websocketpp_client _client;
- unordered_map<string, websocketpp::connection_hdl> _connectionHandles;
- mutex _propertyMutex;
+void WebsocketppClient::receive(
+ string uri,
+ function<void(const string&)> messageHandler)
+{
+ lock_guard<mutex> lock(this->_propertyMutex);
+ auto connectionHandle = this->_connectionHandles[uri];
+ auto connection = this->_client.get_con_from_hdl(connectionHandle);
- void onMessage(websocketpp::connection_hdl handle, websocketpp_client::message_ptr message)
+ connection->set_message_handler([messageHandler](
+ websocketpp::connection_hdl connectionHandle,
+ websocketpp_client::message_ptr message)
{
- };
+ messageHandler(message->get_payload());
+ });
+};
+
+void WebsocketppClient::closeConnection(string uri)
+{
+ lock_guard<mutex> lock(this->_propertyMutex);
+
+ websocketpp::connection_hdl handle = this->_connectionHandles[uri];
+ this->_client.close(
+ handle,
+ websocketpp::close::status::going_away,
+ "_client requested close.");
+
+ this->_connectionHandles.erase(uri);
};
-} // namespace client
diff --git a/src/event.cpp b/src/data/event.cpp
index 703efae..a1a96f1 100644
--- a/src/event.cpp
+++ b/src/data/event.cpp
@@ -1,12 +1,13 @@
-#include <ctime>
+#include <sstream>
+#include <stdexcept>
-#include "nostr.hpp"
+#include "data/data.hpp"
using namespace nlohmann;
+using namespace nostr::data;
using namespace std;
-namespace nostr
-{
+// TODO: Verify event signature using noscrypt.
string Event::serialize()
{
try
@@ -19,12 +20,13 @@ string Event::serialize()
}
json j = {
- {"pubkey", this->pubkey},
- {"created_at", this->createdAt},
- {"kind", this->kind},
- {"tags", this->tags},
- {"content", this->content},
- {"sig", this->sig}};
+ { "pubkey", this->pubkey },
+ { "created_at", this->createdAt },
+ { "kind", this->kind },
+ { "tags", this->tags },
+ { "content", this->content },
+ { "sig", this->sig }
+ };
j["id"] = this->generateId(j.dump());
@@ -88,12 +90,6 @@ void Event::validate()
{
throw std::invalid_argument("Event::validate: A valid event kind is required.");
}
-
- bool hasSignature = this->sig.length() > 0;
- if (!hasSignature)
- {
- throw std::invalid_argument("Event::validate: The event must be signed.");
- }
};
string Event::generateId(string serializedData) const
@@ -123,4 +119,3 @@ bool Event::operator==(const Event& other) const
return this->id == other.id;
};
-} // namespace nostr
diff --git a/src/filters.cpp b/src/data/filters.cpp
index 6f62e0b..b725002 100644
--- a/src/filters.cpp
+++ b/src/data/filters.cpp
@@ -1,10 +1,11 @@
-#include "nostr.hpp"
+#include <stdexcept>
+
+#include "data/data.hpp"
using namespace nlohmann;
+using namespace nostr::data;
using namespace std;
-namespace nostr
-{
string Filters::serialize(string& subscriptionId)
{
try
@@ -17,12 +18,13 @@ string Filters::serialize(string& subscriptionId)
}
json j = {
- {"ids", this->ids},
- {"authors", this->authors},
- {"kinds", this->kinds},
- {"since", this->since},
- {"until", this->until},
- {"limit", this->limit}};
+ { "ids", this->ids },
+ { "authors", this->authors },
+ { "kinds", this->kinds },
+ { "since", this->since },
+ { "until", this->until },
+ { "limit", this->limit }
+ };
for (auto& tag : this->tags)
{
@@ -64,4 +66,3 @@ void Filters::validate()
throw invalid_argument("Filters::validate: At least one filter must be set.");
}
};
-} // namespace nostr
diff --git a/src/nostr_service.cpp b/src/service/nostr_service_base.cpp
index 5adca3b..26748e0 100644
--- a/src/nostr_service.cpp
+++ b/src/service/nostr_service_base.cpp
@@ -1,54 +1,60 @@
-#include "nostr.hpp"
-#include "client/web_socket_client.hpp"
+#include <exception>
+#include <future>
+#include <stdexcept>
+#include <thread>
+
+#include <uuid_v4.h>
+
+#include "service/nostr_service_base.hpp"
using namespace nlohmann;
+using namespace nostr::service;
using namespace std;
-namespace nostr
-{
-NostrService::NostrService(
+NostrServiceBase::NostrServiceBase(
shared_ptr<plog::IAppender> appender,
- shared_ptr<client::IWebSocketClient> client,
- shared_ptr<ISigner> signer)
-: NostrService(appender, client, signer, {}) { };
+ shared_ptr<client::IWebSocketClient> client)
+: NostrServiceBase(appender, client, {}) { };
-NostrService::NostrService(
+NostrServiceBase::NostrServiceBase(
shared_ptr<plog::IAppender> appender,
shared_ptr<client::IWebSocketClient> client,
- shared_ptr<ISigner> signer,
vector<string> relays)
-: _defaultRelays(relays), _client(client), _signer(signer)
+: _defaultRelays(relays), _client(client)
{
plog::init(plog::debug, appender.get());
client->start();
};
-NostrService::~NostrService()
+NostrServiceBase::~NostrServiceBase()
{
this->_client->stop();
};
-vector<string> NostrService::defaultRelays() const { return this->_defaultRelays; };
+vector<string> NostrServiceBase::defaultRelays() const
+{ return this->_defaultRelays; };
-vector<string> NostrService::activeRelays() const { return this->_activeRelays; };
+vector<string> NostrServiceBase::activeRelays() const
+{ return this->_activeRelays; };
-unordered_map<string, vector<string>> NostrService::subscriptions() const { return this->_subscriptions; };
+unordered_map<string, vector<string>> NostrServiceBase::subscriptions() const
+{ return this->_subscriptions; };
-vector<string> NostrService::openRelayConnections()
+vector<string> NostrServiceBase::openRelayConnections()
{
return this->openRelayConnections(this->_defaultRelays);
};
-vector<string> NostrService::openRelayConnections(vector<string> relays)
+vector<string> NostrServiceBase::openRelayConnections(vector<string> relays)
{
PLOG_INFO << "Attempting to connect to Nostr relays.";
- vector<string> unconnectedRelays = this->getUnconnectedRelays(relays);
+ vector<string> unconnectedRelays = this->_getUnconnectedRelays(relays);
vector<thread> connectionThreads;
for (string relay : unconnectedRelays)
{
thread connectionThread([this, relay]() {
- this->connect(relay);
+ this->_connect(relay);
});
connectionThreads.push_back(move(connectionThread));
}
@@ -58,15 +64,15 @@ vector<string> NostrService::openRelayConnections(vector<string> relays)
connectionThread.join();
}
- size_t targetCount = relays.size();
- size_t activeCount = this->_activeRelays.size();
+ std::size_t targetCount = relays.size();
+ std::size_t activeCount = this->_activeRelays.size();
PLOG_INFO << "Connected to " << activeCount << "/" << targetCount << " target relays.";
// This property should only contain successful relays at this point.
return this->_activeRelays;
};
-void NostrService::closeRelayConnections()
+void NostrServiceBase::closeRelayConnections()
{
if (this->_activeRelays.size() == 0)
{
@@ -77,16 +83,16 @@ void NostrService::closeRelayConnections()
this->closeRelayConnections(this->_activeRelays);
};
-void NostrService::closeRelayConnections(vector<string> relays)
+void NostrServiceBase::closeRelayConnections(vector<string> relays)
{
PLOG_INFO << "Disconnecting from Nostr relays.";
- vector<string> connectedRelays = getConnectedRelays(relays);
+ vector<string> connectedRelays = this->_getConnectedRelays(relays);
vector<thread> disconnectionThreads;
for (string relay : connectedRelays)
{
thread disconnectionThread([this, relay]() {
- this->disconnect(relay);
+ this->_disconnect(relay);
});
disconnectionThreads.push_back(move(disconnectionThread));
@@ -102,7 +108,8 @@ void NostrService::closeRelayConnections(vector<string> relays)
};
// TODO: Make this method return a promise.
-tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Event> event)
+tuple<vector<string>, vector<string>> NostrServiceBase::publishEvent(
+ shared_ptr<nostr::data::Event> event)
{
vector<string> successfulRelays;
vector<string> failedRelays;
@@ -112,7 +119,6 @@ tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Even
json message;
try
{
- this->_signer->sign(event);
message = json::array({ "EVENT", event->serialize() });
}
catch (const std::invalid_argument& e)
@@ -139,7 +145,7 @@ tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Even
relay,
[this, &relay, &event, &publishPromise](string response)
{
- this->onAcceptance(response, [this, &relay, &event, &publishPromise](bool isAccepted)
+ this->_onAcceptance(response, [this, &relay, &event, &publishPromise](bool isAccepted)
{
if (isAccepted)
{
@@ -174,119 +180,128 @@ tuple<vector<string>, vector<string>> NostrService::publishEvent(shared_ptr<Even
}
}
- size_t targetCount = targetRelays.size();
- size_t successfulCount = successfulRelays.size();
+ std::size_t targetCount = targetRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Published event to " << successfulCount << "/" << targetCount << " target relays.";
return make_tuple(successfulRelays, failedRelays);
};
-// TODO: Make this method return a promise.
// TODO: Add a timeout to this method to prevent hanging while waiting for the relay.
-vector<shared_ptr<Event>> NostrService::queryRelays(shared_ptr<Filters> filters)
+future<vector<shared_ptr<nostr::data::Event>>> NostrServiceBase::queryRelays(
+ shared_ptr<nostr::data::Filters> filters)
{
- if (filters->limit > 64 || filters->limit < 1)
+ return async(launch::async, [this, filters]() -> vector<shared_ptr<Event>>
{
- PLOG_WARNING << "Filters limit must be between 1 and 64, inclusive. Setting limit to 16.";
- filters->limit = 16;
- }
-
- vector<shared_ptr<Event>> events;
-
- string subscriptionId = this->generateSubscriptionId();
- string request;
-
- try
- {
- request = filters->serialize(subscriptionId);
- }
- catch (const invalid_argument& e)
- {
- PLOG_ERROR << "Failed to serialize filters - invalid object: " << e.what();
- throw e;
- }
- catch (const json::exception& je)
- {
- PLOG_ERROR << "Failed to serialize filters - JSON exception: " << je.what();
- throw je;
- }
+ if (filters->limit > 64 || filters->limit < 1)
+ {
+ PLOG_WARNING << "Filters limit must be between 1 and 64, inclusive. Setting limit to 16.";
+ filters->limit = 16;
+ }
- vector<future<tuple<string, bool>>> requestFutures;
+ vector<shared_ptr<Event>> events;
- // Send the same query to each relay. As events trickle in from each relay, they will be added
- // to the events vector. Multiple copies of an event may be received if the same event is
- // stored on multiple relays. The function will block until all of the relays send an EOSE or
- // CLOSE message.
- for (const string relay : this->_activeRelays)
- {
- promise<tuple<string, bool>> eosePromise;
- requestFutures.push_back(move(eosePromise.get_future()));
+ string subscriptionId = this->generateSubscriptionId();
+ string request;
- auto [uri, success] = this->_client->send(
- request,
- relay,
- [this, &relay, &events, &eosePromise](string payload)
- {
- this->onSubscriptionMessage(
- payload,
- [&events](const string&, shared_ptr<Event> event)
- {
- events.push_back(event);
- },
- [relay, &eosePromise](const string&)
- {
- eosePromise.set_value(make_tuple(relay, true));
- },
- [relay, &eosePromise](const string&, const string&)
- {
- eosePromise.set_value(make_tuple(relay, false));
- });
- });
-
- if (success)
+ try
{
- PLOG_INFO << "Sent query to relay " << relay;
- lock_guard<mutex> lock(this->_propertyMutex);
- this->_subscriptions[subscriptionId].push_back(relay);
+ request = filters->serialize(subscriptionId);
}
- else
+ catch (const invalid_argument& e)
{
- PLOG_WARNING << "Failed to send query to relay " << relay;
- eosePromise.set_value(make_tuple(uri, false));
+ PLOG_ERROR << "Failed to serialize filters - invalid object: " << e.what();
+ throw e;
}
- }
-
- // Close open subscriptions and disconnect from relays after events are received.
- for (auto& publishFuture : requestFutures)
- {
- auto [relay, isEose] = publishFuture.get();
- if (isEose)
+ catch (const json::exception& je)
{
- PLOG_INFO << "Received EOSE message from relay " << relay;
+ PLOG_ERROR << "Failed to serialize filters - JSON exception: " << je.what();
+ throw je;
}
- else
+
+ vector<future<tuple<string, bool>>> requestFutures;
+
+ unordered_set<string> uniqueEventIds;
+
+ // Send the same query to each relay. As events trickle in from each relay, they will be added
+ // to the events vector. Duplicate copies of the same event will be ignored, as events are
+ // stored on multiple relays. The function will block until all of the relays send an EOSE or
+ // CLOSE message.
+ for (const string relay : this->_activeRelays)
{
- PLOG_WARNING << "Received CLOSE message from relay " << relay;
- this->closeRelayConnections({ relay });
+ promise<tuple<string, bool>> eosePromise;
+ requestFutures.push_back(move(eosePromise.get_future()));
+
+ auto [uri, success] = this->_client->send(
+ request,
+ relay,
+ [this, &relay, &events, &eosePromise, &uniqueEventIds](string payload)
+ {
+ this->onSubscriptionMessage(
+ payload,
+ [&events, &uniqueEventIds](const string&, shared_ptr<Event> event)
+ {
+ // Check if the event is unique before adding.
+ if (uniqueEventIds.insert(event->id).second)
+ {
+ events.push_back(event);
+ }
+ },
+ [relay, &eosePromise](const string&)
+ {
+ eosePromise.set_value(make_tuple(relay, true));
+ },
+ [relay, &eosePromise](const string&, const string&)
+ {
+ eosePromise.set_value(make_tuple(relay, false));
+ });
+ });
+
+ if (success)
+ {
+ PLOG_INFO << "Sent query to relay " << relay;
+ lock_guard<mutex> lock(this->_propertyMutex);
+ this->_subscriptions[subscriptionId].push_back(relay);
+ }
+ else
+ {
+ PLOG_WARNING << "Failed to send query to relay " << relay;
+ eosePromise.set_value(make_tuple(uri, false));
+ }
}
- }
- this->closeSubscription(subscriptionId);
- // TODO: De-duplicate events in the vector before returning.
- return events;
+ // Close open subscriptions and disconnect from relays after events are received.
+
+ for (auto& publishFuture : requestFutures)
+ {
+ auto [relay, isEose] = publishFuture.get();
+ if (isEose)
+ {
+ PLOG_INFO << "Received EOSE message from relay " << relay;
+ }
+ else
+ {
+ PLOG_WARNING << "Received CLOSE message from relay " << relay;
+ this->closeRelayConnections({ relay });
+ }
+ }
+ this->closeSubscription(subscriptionId);
+
+ return events;
+ });
};
-string NostrService::queryRelays(
- shared_ptr<Filters> filters,
- function<void(const string&, shared_ptr<Event>)> eventHandler,
+string NostrServiceBase::queryRelays(
+ shared_ptr<nostr::data::Filters> filters,
+ function<void(const string&, shared_ptr<nostr::data::Event>)> eventHandler,
function<void(const string&)> eoseHandler,
function<void(const string&, const string&)> closeHandler)
{
vector<string> successfulRelays;
vector<string> failedRelays;
- string subscriptionId = this->generateSubscriptionId();
+ string subscriptionId = this->_generateSubscriptionId();
string request = filters->serialize(subscriptionId);
vector<future<tuple<string, bool>>> requestFutures;
for (const string relay : this->_activeRelays)
@@ -303,7 +318,7 @@ string NostrService::queryRelays(
relay,
[this, &eventHandler, &eoseHandler, &closeHandler](string payload)
{
- this->onSubscriptionMessage(payload, eventHandler, eoseHandler, closeHandler);
+ this->_onSubscriptionMessage(payload, eventHandler, eoseHandler, closeHandler);
});
});
requestFutures.push_back(move(requestFuture));
@@ -322,20 +337,20 @@ string NostrService::queryRelays(
}
}
- size_t targetCount = this->_activeRelays.size();
- size_t successfulCount = successfulRelays.size();
+ std::size_t targetCount = this->_activeRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Sent query to " << successfulCount << "/" << targetCount << " open relay connections.";
return subscriptionId;
};
-tuple<vector<string>, vector<string>> NostrService::closeSubscription(string subscriptionId)
+tuple<vector<string>, vector<string>> NostrServiceBase::closeSubscription(string subscriptionId)
{
vector<string> successfulRelays;
vector<string> failedRelays;
vector<string> subscriptionRelays;
- size_t subscriptionRelayCount;
+ std::size_t subscriptionRelayCount;
vector<future<tuple<string, bool>>> closeFutures;
try
@@ -375,7 +390,7 @@ tuple<vector<string>, vector<string>> NostrService::closeSubscription(string sub
}
}
- size_t successfulCount = successfulRelays.size();
+ std::size_t successfulCount = successfulRelays.size();
PLOG_INFO << "Sent CLOSE request for subscription " << subscriptionId << " to " << successfulCount << "/" << subscriptionRelayCount << " open relay connections.";
// If there were no failures, and the subscription has been closed on all of its relays, forget
@@ -389,21 +404,21 @@ tuple<vector<string>, vector<string>> NostrService::closeSubscription(string sub
return make_tuple(successfulRelays, failedRelays);
};
-bool NostrService::closeSubscription(string subscriptionId, string relay)
+bool NostrServiceBase::closeSubscription(string subscriptionId, string relay)
{
- if (!this->hasSubscription(subscriptionId, relay))
+ if (!this->_hasSubscription(subscriptionId, relay))
{
PLOG_WARNING << "Subscription " << subscriptionId << " not found on relay " << relay;
return false;
}
- if (!this->isConnected(relay))
+ if (!this->_isConnected(relay))
{
PLOG_WARNING << "Relay " << relay << " is not connected.";
return false;
}
- string request = this->generateCloseRequest(subscriptionId);
+ string request = this->_generateCloseRequest(subscriptionId);
auto [uri, success] = this->_client->send(request, relay);
if (success)
@@ -429,7 +444,7 @@ bool NostrService::closeSubscription(string subscriptionId, string relay)
return success;
};
-vector<string> NostrService::closeSubscriptions()
+vector<string> NostrServiceBase::closeSubscriptions()
{
unique_lock<mutex> lock(this->_propertyMutex);
vector<string> subscriptionIds;
@@ -452,7 +467,7 @@ vector<string> NostrService::closeSubscriptions()
return remainingSubscriptions;
};
-vector<string> NostrService::getConnectedRelays(vector<string> relays)
+vector<string> NostrServiceBase::_getConnectedRelays(vector<string> relays)
{
PLOG_VERBOSE << "Identifying connected relays.";
vector<string> connectedRelays;
@@ -469,7 +484,7 @@ vector<string> NostrService::getConnectedRelays(vector<string> relays)
}
else if (isActive && !isConnected)
{
- this->eraseActiveRelay(relay);
+ this->_eraseActiveRelay(relay);
}
else if (!isActive && isConnected)
{
@@ -480,7 +495,7 @@ vector<string> NostrService::getConnectedRelays(vector<string> relays)
return connectedRelays;
};
-vector<string> NostrService::getUnconnectedRelays(vector<string> relays)
+vector<string> NostrServiceBase::_getUnconnectedRelays(vector<string> relays)
{
PLOG_VERBOSE << "Identifying unconnected relays.";
vector<string> unconnectedRelays;
@@ -499,7 +514,7 @@ vector<string> NostrService::getUnconnectedRelays(vector<string> relays)
else if (isActive && !isConnected)
{
PLOG_VERBOSE << "Relay " << relay << " is active but not connected. Removing from active relays list.";
- this->eraseActiveRelay(relay);
+ this->_eraseActiveRelay(relay);
unconnectedRelays.push_back(relay);
}
else if (!isActive && isConnected)
@@ -511,7 +526,7 @@ vector<string> NostrService::getUnconnectedRelays(vector<string> relays)
return unconnectedRelays;
};
-bool NostrService::isConnected(string relay)
+bool NostrServiceBase::_isConnected(string relay)
{
auto it = find(this->_activeRelays.begin(), this->_activeRelays.end(), relay);
if (it != this->_activeRelays.end()) // If the relay is in this->_activeRelays
@@ -521,7 +536,7 @@ bool NostrService::isConnected(string relay)
return false;
};
-void NostrService::eraseActiveRelay(string relay)
+void NostrServiceBase::_eraseActiveRelay(string relay)
{
auto it = find(this->_activeRelays.begin(), this->_activeRelays.end(), relay);
if (it != this->_activeRelays.end()) // If the relay is in this->_activeRelays
@@ -530,7 +545,7 @@ void NostrService::eraseActiveRelay(string relay)
}
};
-void NostrService::connect(string relay)
+void NostrServiceBase::_connect(string relay)
{
PLOG_VERBOSE << "Connecting to relay " << relay;
this->_client->openConnection(relay);
@@ -549,28 +564,28 @@ void NostrService::connect(string relay)
}
};
-void NostrService::disconnect(string relay)
+void NostrServiceBase::_disconnect(string relay)
{
this->_client->closeConnection(relay);
lock_guard<mutex> lock(this->_propertyMutex);
- this->eraseActiveRelay(relay);
+ this->_eraseActiveRelay(relay);
};
-string NostrService::generateSubscriptionId()
+string NostrServiceBase::_generateSubscriptionId()
{
UUIDv4::UUIDGenerator<std::mt19937_64> uuidGenerator;
UUIDv4::UUID uuid = uuidGenerator.getUUID();
return uuid.str();
};
-string NostrService::generateCloseRequest(string subscriptionId)
+string NostrServiceBase::_generateCloseRequest(string subscriptionId)
{
json jarr = json::array({ "CLOSE", subscriptionId });
return jarr.dump();
};
-bool NostrService::hasSubscription(string subscriptionId)
+bool NostrServiceBase::_hasSubscription(string subscriptionId)
{
lock_guard<mutex> lock(this->_propertyMutex);
auto it = this->_subscriptions.find(subscriptionId);
@@ -578,7 +593,7 @@ bool NostrService::hasSubscription(string subscriptionId)
return it != this->_subscriptions.end();
};
-bool NostrService::hasSubscription(string subscriptionId, string relay)
+bool NostrServiceBase::_hasSubscription(string subscriptionId, string relay)
{
lock_guard<mutex> lock(this->_propertyMutex);
auto subscriptionIt = this->_subscriptions.find(subscriptionId);
@@ -594,9 +609,9 @@ bool NostrService::hasSubscription(string subscriptionId, string relay)
return relayIt != relays.end();
};
-void NostrService::onSubscriptionMessage(
+void NostrServiceBase::_onSubscriptionMessage(
string message,
- function<void(const string&, shared_ptr<Event>)> eventHandler,
+ function<void(const string&, shared_ptr<nostr::data::Event>)> eventHandler,
function<void(const string&)> eoseHandler,
function<void(const string&, const string&)> closeHandler)
{
@@ -607,8 +622,8 @@ void NostrService::onSubscriptionMessage(
if (messageType == "EVENT")
{
string subscriptionId = jMessage.at(1);
- Event event = Event::fromString(jMessage.at(2));
- eventHandler(subscriptionId, make_shared<Event>(event));
+ nostr::data::Event event = nostr::data::Event::fromString(jMessage.at(2));
+ eventHandler(subscriptionId, make_shared<nostr::data::Event>(event));
}
else if (messageType == "EOSE")
{
@@ -639,7 +654,9 @@ void NostrService::onSubscriptionMessage(
}
};
-void NostrService::onAcceptance(string message, function<void(const bool)> acceptanceHandler)
+void NostrServiceBase::_onAcceptance(
+ string message,
+ function<void(const bool)> acceptanceHandler)
{
try
{
@@ -657,4 +674,3 @@ void NostrService::onAcceptance(string message, function<void(const bool)> accep
throw je;
}
};
-} // namespace nostr
diff --git a/src/signer/noscrypt_signer.cpp b/src/signer/noscrypt_signer.cpp
new file mode 100644
index 0000000..68cdf6a
--- /dev/null
+++ b/src/signer/noscrypt_signer.cpp
@@ -0,0 +1,697 @@
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <memory>
+#include <random>
+#include <sstream>
+#include <tuple>
+
+#include <nlohmann/json.hpp>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <uuid_v4.h>
+
+#include "signer/noscrypt_signer.hpp"
+
+using namespace nostr::data;
+using namespace nostr::service;
+using namespace nostr::signer;
+using namespace std;
+
+#pragma region Constructors and Destructors
+
+NoscryptSigner::NoscryptSigner(
+ shared_ptr<plog::IAppender> appender,
+ shared_ptr<INostrServiceBase> nostrService)
+{
+ plog::init(plog::debug, appender.get());
+
+ this->_reseedRandomNumberGenerator();
+ this->_initNoscryptContext();
+ this->_createLocalKeypair();
+
+ this->_nostrService = nostrService;
+};
+
+NoscryptSigner::~NoscryptSigner()
+{
+ NCDestroyContext(this->_noscryptContext.get());
+};
+
+#pragma endregion
+
+#pragma region Public Interface
+
+void NoscryptSigner::receiveConnection(string connectionToken)
+{
+ if (connectionToken.empty())
+ {
+ PLOG_ERROR << "No connection token was provided - unable to connect to a remote signer.";
+ return;
+ }
+
+ int queryStart = this->_parseRemotePublicKey(connectionToken);
+ if (queryStart == -1)
+ {
+ return;
+ }
+
+ string remainingToken = connectionToken.substr(queryStart);
+ int splitIndex = remainingToken.find('&');
+ do
+ {
+ string param = remainingToken.substr(0, splitIndex);
+ this->_handleConnectionTokenParam(param);
+
+ remainingToken = remainingToken.substr(splitIndex + 1);
+ splitIndex = remainingToken.find('&');
+ } while (splitIndex != string::npos);
+
+ // TODO: Handle any messaging with the remote signer.
+};
+
+string NoscryptSigner::initiateConnection(
+ vector<string> relays,
+ string name,
+ string url,
+ string description)
+{
+ // Return an empty string if the local keypair is invalid.
+ if (this->_getLocalPrivateKey().empty() || this->_getLocalPublicKey().empty())
+ {
+ PLOG_ERROR << "A valid local keypair is required to connect to a remote signer.";
+ return string();
+ }
+
+ // Return an empty string if no relays are provided.
+ if (relays.empty())
+ {
+ PLOG_ERROR << "At least one relay must be provided to connect to a remote signer.";
+ return string();
+ }
+
+ // Store the provided relay list to be used for sending and receving connection events.
+ this->_relays = relays;
+
+ // Generate the connection token.
+ stringstream ss;
+ ss << "nostrconnect://" << this->_localPublicKey;
+ for (int i = 0; i < relays.size(); i++)
+ {
+ ss << (i == 0 ? "?" : "&");
+ ss << "relay=" << relays[i];
+ }
+ ss << "&metadata={";
+ ss << "\"name\":\"" << name << "\",";
+ ss << "\"url\":\"" << url << "\",";
+ ss << "\"description\":\"" << description << "\"";
+ ss << "}";
+
+ return ss.str();
+
+ // TODO: Handle any messaging with the remote signer.
+};
+
+shared_ptr<promise<bool>> NoscryptSigner::sign(shared_ptr<Event> event)
+{
+ auto signingPromise = make_shared<promise<bool>>();
+
+ auto signerAvailable = this->_pingSigner().get_future();
+ if (signerAvailable.get() == false)
+ {
+ PLOG_ERROR << "Ping to the remote signer failed - the remote signer may be unavailable.";
+ signingPromise->set_value(false);
+ return signingPromise;
+ }
+
+ // Create the JSON-RPC-like message content.
+ auto params = nlohmann::json::array();
+ params.push_back(event->serialize());
+
+ auto requestId = this->_generateSignerRequestId();
+
+ // Create a filter set to find events from the remote signer.
+ auto remoteSignerFilters = this->_buildSignerMessageFilters();
+
+ // Generate the signing request event.
+ nlohmann::json jrpc = {
+ { "id", requestId },
+ { "method", "sign_event" },
+ { "params", params }
+ };
+ auto signingRequest = this->_wrapSignerMessage(jrpc);
+
+ // Send the signing request.
+ this->_nostrService->publishEvent(signingRequest);
+
+ // Wait for the remote signer's response.
+ this->_nostrService->queryRelays(
+ remoteSignerFilters,
+ [this, &event, &signingPromise](const string&, shared_ptr<Event> signerEvent)
+ {
+ // Assign the response event to the `event` parameter, accomplishing the intended
+ // function result via side effect.
+ string signerResponse = this->_unwrapSignerMessage(signerEvent);
+ event = make_shared<Event>(Event::fromString(signerResponse));
+ signingPromise->set_value(true);
+ },
+ [&signingPromise](const string&)
+ {
+ signingPromise->set_value(false);
+ },
+ [&signingPromise](const string&, const string&)
+ {
+ signingPromise->set_value(false);
+ });
+
+ return signingPromise;
+};
+
+#pragma endregion
+
+#pragma region Private Accessors
+
+inline string NoscryptSigner::_getLocalPrivateKey() const
+{
+ stringstream privkeyStream;
+ for (int i = 0; i < sizeof(NCSecretKey); i++)
+ {
+ privkeyStream << hex << setw(2) << setfill('0') << static_cast<int>(this->_localPrivateKey->key[i]);
+ }
+
+ return privkeyStream.str();
+};
+
+inline void NoscryptSigner::_setLocalPrivateKey(const string value)
+{
+ auto seckey = make_unique<NCSecretKey>();
+
+ for (auto i = 0; i < sizeof(NCSecretKey); i++)
+ {
+ stringstream ss;
+ ss << hex << value.substr(i * 2, 2);
+ uint8_t byte;
+ ss >> byte;
+ seckey->key[i] = byte;
+ }
+
+ this->_localPrivateKey = move(seckey);
+};
+
+inline string NoscryptSigner::_getLocalPublicKey() const
+{
+ stringstream pubkeyStream;
+ for (int i = 0; i < sizeof(NCPublicKey); i++)
+ {
+ pubkeyStream << hex << setw(2) << setfill('0') << static_cast<int>(this->_localPublicKey->key[i]);
+ }
+
+ return pubkeyStream.str();
+};
+
+inline void NoscryptSigner::_setLocalPublicKey(const string value)
+{
+ auto pubkey = make_unique<NCPublicKey>();
+
+ for (auto i = 0; i < sizeof(NCPublicKey); i++)
+ {
+ stringstream ss;
+ ss << hex << value.substr(i * 2, 2);
+ uint8_t byte;
+ ss >> byte;
+ pubkey->key[i] = byte;
+ }
+
+ this->_localPublicKey = move(pubkey);
+};
+
+inline string NoscryptSigner::_getRemotePublicKey() const
+{
+ stringstream pubkeyStream;
+ for (int i = 0; i < sizeof(NCPublicKey); i++)
+ {
+ pubkeyStream << hex << setw(2) << setfill('0') << static_cast<int>(this->_remotePublicKey->key[i]);
+ }
+
+ return pubkeyStream.str();
+};
+
+inline void NoscryptSigner::_setRemotePublicKey(const string value)
+{
+ auto pubkey = make_unique<NCPublicKey>();
+
+ for (auto i = 0; i < sizeof(NCPublicKey); i++)
+ {
+ stringstream ss;
+ ss << hex << value.substr(i * 2, 2);
+ uint8_t byte;
+ ss >> byte;
+ pubkey->key[i] = byte;
+ }
+
+ this->_remotePublicKey = move(pubkey);
+};
+
+#pragma endregion
+
+#pragma region Setup
+
+void NoscryptSigner::_initNoscryptContext()
+{
+ shared_ptr<NCContext> context;
+ auto contextStructSize = NCGetContextStructSize();
+ auto randomEntropy = make_unique<uint8_t>(contextStructSize);
+
+ random_device rd;
+ mt19937 gen(rd());
+ uniform_int_distribution<> dist(0, contextStructSize);
+ generate_n(randomEntropy.get(), contextStructSize, [&]() { return dist(gen); });
+
+ NCResult initResult = NCInitContext(context.get(), randomEntropy.get());
+ this->_logNoscryptInitResult(initResult);
+
+ this->_noscryptContext = move(context);
+};
+
+/**
+ * @brief Generates a private/public key pair for local use.
+ * @returns The generated keypair of the form `[privateKey, publicKey]`, or a pair of empty
+ * strings if the function failed.
+ * @remarks This keypair is intended for temporary use, and should not be saved or used outside
+ * of this class.
+ */
+void NoscryptSigner::_createLocalKeypair()
+{
+ string privateKey;
+ string publicKey;
+
+ // To generate a private key, all we need is a random 32-bit buffer.
+ auto secret = make_unique<NCSecretKey>();
+
+ // Loop attempts to generate a secret key until a valid key is produced.
+ // Limit the number of attempts to prevent resource exhaustion in the event of a failure.
+ NCResult secretValidationResult;
+ int loopCount = 0;
+ do
+ {
+ int rc = RAND_bytes(secret->key, sizeof(NCSecretKey));
+ if (rc != 1)
+ {
+ unsigned long err = ERR_get_error();
+ PLOG_ERROR << "OpenSSL error " << err << " occurred while generating a secret key.";
+ }
+
+ secretValidationResult = NCValidateSecretKey(this->_noscryptContext.get(), secret.get());
+ } while (secretValidationResult != NC_SUCCESS && ++loopCount < 64);
+
+ this->_logNoscryptSecretValidationResult(secretValidationResult);
+ this->_localPrivateKey = move(secret);
+
+ // Use noscrypt to derive the public key from its private counterpart.
+ auto pubkey = make_unique<NCPublicKey>();
+ NCResult pubkeyGenerationResult = NCGetPublicKey(
+ this->_noscryptContext.get(),
+ secret.get(),
+ pubkey.get());
+ this->_logNoscryptPubkeyGenerationResult(pubkeyGenerationResult);
+ this->_localPublicKey = move(pubkey);
+};
+
+int NoscryptSigner::_parseRemotePublicKey(string connectionToken)
+{
+ int queryStart = connectionToken.find('?');
+ if (queryStart == string::npos)
+ {
+ PLOG_ERROR << "The connection token is invalid - no query string was found.";
+ return -1;
+ }
+
+ const int pubkeyStart = 9;
+ string prefix = connectionToken.substr(0, pubkeyStart);
+ if (prefix != "bunker://")
+ {
+ PLOG_ERROR << "The connection token is invalid - the token must begin with 'bunker://'.";
+ return -1;
+ }
+
+ string remotePubkey = connectionToken.substr(pubkeyStart, queryStart);
+ this->_setRemotePublicKey(remotePubkey);
+
+ return queryStart + 1;
+};
+
+void NoscryptSigner::_handleConnectionTokenParam(string param)
+{
+ // Parse the query param into a key-value pair.
+ int splitIndex = param.find('=');
+ if (splitIndex == string::npos)
+ {
+ PLOG_ERROR << "The connection token query param is invalid - it is not of the form 'key=value'.";
+ return;
+ }
+
+ string key = param.substr(0, splitIndex);
+ string value = param.substr(splitIndex + 1);
+
+ // Handle the key-value pair.
+ if (key == "relay")
+ {
+ this->_relays.push_back(value);
+ }
+ else if (key == "secret")
+ {
+ this->_bunkerSecret = value;
+ }
+};
+
+#pragma endregion
+
+#pragma region Signer Helpers
+
+inline string NoscryptSigner::_generateSignerRequestId() const
+{
+ UUIDv4::UUIDGenerator<std::mt19937_64> uuidGenerator;
+ UUIDv4::UUID uuid = uuidGenerator.getUUID();
+ return uuid.str();
+};
+
+shared_ptr<Event> NoscryptSigner::_wrapSignerMessage(nlohmann::json jrpc)
+{
+ // Encrypt the message payload.
+ string encryptedContent;
+ switch (this->_nostrConnectEncryption)
+ {
+ case Encryption::NIP44:
+ encryptedContent = this->_encryptNip44(jrpc.dump());
+ if (!encryptedContent.empty())
+ {
+ break;
+ }
+
+ // Use NIP-04 encryption as a fallback.
+ case Encryption::NIP04:
+ encryptedContent = this->_encryptNip04(jrpc.dump());
+ break;
+ }
+
+ // Wrap the event to be signed in a signing request event.
+ auto wrapperEvent = make_shared<Event>();
+ wrapperEvent->pubkey = this->_getLocalPublicKey();
+ wrapperEvent->kind = this->_nostrConnectKind;
+ wrapperEvent->tags.push_back({ "p", this->_getRemotePublicKey() });
+ wrapperEvent->content = encryptedContent;
+
+ // Generate a random seed for the signer.
+ auto random32 = make_shared<uint8_t>(32);
+ int code = RAND_bytes(random32.get(), 32);
+ if (code <= 0)
+ {
+ PLOG_ERROR << "Failed to generate a random 32-byte seed buffer for the signer.";
+ return nullptr;
+ }
+
+ // Sign the wrapper message with the local secret key.
+ string serializedEvent = wrapperEvent->serialize();
+ uint32_t dataSize = serializedEvent.length();
+ auto signature = make_unique<uint8_t>(64);
+ NCResult signatureResult = NCSignData(
+ this->_noscryptContext.get(),
+ this->_localPrivateKey.get(),
+ random32.get(),
+ reinterpret_cast<uint8_t*>(serializedEvent.data()),
+ dataSize,
+ signature.get());
+
+ // TODO: Handle result codes.
+ if (signatureResult != NC_SUCCESS)
+ {
+ return nullptr;
+ }
+
+ // Add the signature to the event.
+ wrapperEvent->sig = string((char*)signature.get(), 64);
+
+ return wrapperEvent;
+};
+
+string NoscryptSigner::_unwrapSignerMessage(shared_ptr<Event> event)
+{
+ // TODO: Verify the incoming event.
+
+ // Extract and decrypt the event payload.
+ string encryptedContent = event->content;
+ string decryptedContent;
+
+ // NIP-04 encrypted strings include `?iv=` near the end (source: hodlbod).
+ if (encryptedContent.find("?iv=") != string::npos)
+ {
+ decryptedContent = this->_decryptNip04(encryptedContent);
+ }
+ else
+ {
+ decryptedContent = this->_decryptNip44(encryptedContent);
+ }
+
+ // Parse the decrypted string into a JSON object.
+ return decryptedContent;
+};
+
+inline shared_ptr<Filters> NoscryptSigner::_buildSignerMessageFilters() const
+{
+ auto filters = make_shared<Filters>();
+ filters->authors.push_back(this->_getRemotePublicKey());
+ filters->kinds.push_back(this->_nostrConnectKind);
+ filters->tags["p"] = { this->_getLocalPublicKey() };
+ filters->since = time(nullptr);
+
+ return filters;
+};
+
+promise<bool> NoscryptSigner::_pingSigner()
+{
+ promise<bool> pingPromise;
+
+ // Generate a ping message and wrap it for the signer.
+ nlohmann::json jrpc =
+ {
+ { "id", this->_generateSignerRequestId() },
+ { "method", "ping" },
+ { "params", nlohmann::json::array() }
+ };
+ auto messageEvent = this->_wrapSignerMessage(jrpc);
+
+ // Generate a filter to receive the response.
+ auto pingFilter = this->_buildSignerMessageFilters();
+
+ this->_nostrService->publishEvent(messageEvent);
+
+ // TODO: Handle the relay response.
+ this->_nostrService->queryRelays(
+ pingFilter,
+ [this, &pingPromise](const string&, shared_ptr<Event> pongEvent)
+ {
+ //
+ string pongMessage = this->_unwrapSignerMessage(pongEvent);
+ pingPromise.set_value(pongMessage == "pong");
+ },
+ [&pingPromise](const string&)
+ {
+ pingPromise.set_value(false);
+ },
+ [&pingPromise](const string&, const string&)
+ {
+ pingPromise.set_value(false);
+ });
+
+ return pingPromise;
+};
+
+#pragma endregion
+
+#pragma region Cryptography
+
+void NoscryptSigner::_reseedRandomNumberGenerator(uint32_t bufferSize)
+{
+ int rc = RAND_load_file("/dev/random", bufferSize);
+ if (rc != bufferSize)
+ {
+ PLOG_WARNING << "Failed to reseed the RNG with /dev/random, falling back to /dev/urandom.";
+ RAND_poll();
+ }
+};
+
+string NoscryptSigner::_encryptNip04(std::string input)
+{
+ throw runtime_error("NIP-04 encryption is not yet implemented.");
+};
+
+string NoscryptSigner::_decryptNip04(string input)
+{
+ throw runtime_error("NIP-04 decryption is not yet implemented.");
+};
+
+string NoscryptSigner::_encryptNip44(string input)
+{
+ uint32_t nip44Version = 0x02;
+
+ auto nonce = make_shared<uint8_t>(32);
+ auto hmacKey = make_shared<uint8_t>(32);
+
+ uint32_t bufferSize = input.length();
+ auto output = make_shared<uint8_t>(bufferSize);
+
+ // Generate a nonce to use for the encryption.
+ int code = RAND_bytes(nonce.get(), 32);
+ if (code <= 0)
+ {
+ PLOG_ERROR << "Failed to generate a nonce for NIP-44 encryption.";
+ return string();
+ }
+
+ // Setup the encryption context.
+ auto encryptionArgs = make_unique<NCEncryptionArgs>(NCEncryptionArgs
+ {
+ nonce.get(),
+ hmacKey.get(),
+ reinterpret_cast<uint8_t*>(input.data()),
+ output.get(),
+ bufferSize,
+ nip44Version
+ });
+
+ // Perform the encryption.
+ NCResult encryptionResult = NCEncrypt(
+ this->_noscryptContext.get(),
+ this->_localPrivateKey.get(),
+ this->_remotePublicKey.get(),
+ encryptionArgs.get());
+
+ // TODO: Handle various codes.
+ if (encryptionResult != NC_SUCCESS)
+ {
+ return string();
+ }
+
+ return string((char*)output.get(), bufferSize);
+};
+
+string NoscryptSigner::_decryptNip44(string input)
+{
+ uint32_t nip44Version = 0x02;
+
+ auto nonce = make_shared<uint8_t>(32);
+ auto hmacKey = make_shared<uint8_t>(32);
+
+ uint32_t bufferSize = input.length();
+ auto output = make_shared<uint8_t>(bufferSize);
+
+ // Generate a nonce to use for the decryption.
+ int code = RAND_bytes(nonce.get(), 32);
+ if (code <= 0)
+ {
+ PLOG_ERROR << "Failed to generate a nonce for NIP-44 decryption.";
+ return string();
+ }
+
+ // Set up the decryption context.
+ auto decryptionArgs = make_unique<NCEncryptionArgs>(NCEncryptionArgs
+ {
+ nonce.get(),
+ hmacKey.get(),
+ reinterpret_cast<uint8_t*>(input.data()),
+ output.get(),
+ bufferSize,
+ nip44Version
+ });
+
+ // Perform the decryption.
+ NCResult decryptionResult = NCDecrypt(
+ this->_noscryptContext.get(),
+ this->_localPrivateKey.get(),
+ this->_remotePublicKey.get(),
+ decryptionArgs.get());
+
+ // TODO: Handle various codes.
+ if (decryptionResult != NC_SUCCESS)
+ {
+ return string();
+ }
+
+ return string((char*)output.get(), bufferSize);
+};
+
+#pragma endregion
+
+#pragma region Logging
+
+inline void NoscryptSigner::_logNoscryptInitResult(NCResult initResult) const
+{
+ switch (initResult) {
+ case NC_SUCCESS:
+ PLOG_INFO << "noscrypt - success";
+ break;
+
+ case E_NULL_PTR:
+ PLOG_ERROR << "noscrypt - error: A null pointer was passed to the initializer.";
+ break;
+
+ case E_INVALID_ARG:
+ PLOG_ERROR << "noscrypt - error: An invalid argument was passed to the initializer.";
+ break;
+
+ case E_INVALID_CONTEXT:
+ PLOG_ERROR << "noscrypt - error: The NCContext struct is in an invalid state.";
+ break;
+
+ case E_ARGUMENT_OUT_OF_RANGE:
+ PLOG_ERROR << "noscrypt - error: An initializer argument was outside the range of acceptable values.";
+ break;
+
+ case E_OPERATION_FAILED:
+ PLOG_ERROR << "noscrypt - error";
+ break;
+ }
+};
+
+inline void NoscryptSigner::_logNoscryptSecretValidationResult(NCResult secretValidationResult) const
+{
+ if (secretValidationResult == NC_SUCCESS)
+ {
+ PLOG_INFO << "noscrypt_signer - success: Generated a valid secret key.";
+ }
+ else
+ {
+ PLOG_ERROR << "noscrypt_signer - error: Failed to generate a valid secret key.";
+ }
+};
+
+inline void NoscryptSigner::_logNoscryptPubkeyGenerationResult(NCResult pubkeyGenerationResult) const
+{
+ switch (pubkeyGenerationResult) {
+ case NC_SUCCESS:
+ PLOG_INFO << "noscrypt - success: Generated a valid public key.";
+ break;
+
+ case E_NULL_PTR:
+ PLOG_ERROR << "noscrypt - error: A null pointer was passed to the public key generation function.";
+ break;
+
+ case E_INVALID_ARG:
+ PLOG_ERROR << "noscrypt - error: An invalid argument was passed to the public key generation function.";
+ break;
+
+ case E_INVALID_CONTEXT:
+ PLOG_ERROR << "noscrypt - error: The NCContext struct is in an invalid state.";
+ break;
+
+ case E_ARGUMENT_OUT_OF_RANGE:
+ PLOG_ERROR << "noscrypt - error: An argument was outside the range of acceptable values.";
+ break;
+
+ case E_OPERATION_FAILED:
+ PLOG_ERROR << "noscrypt - error: Failed to generate the public key from the secret key.";
+ break;
+ }
+};
+
+#pragma endregion