Mads
Multi-Agent Distributed System
Loading...
Searching...
No Matches
curve.hpp
Go to the documentation of this file.
1/*
2 ____ _ _ ______ _______ _ _
3 / ___| | | | _ \ \ / / ____| __ _ _ _| |_| |__
4 | | | | | | |_) \ \ / /| _| / _` | | | | __| '_ \
5 | |___| |_| | _ < \ V / | |___ | (_| | |_| | |_| | | |
6 \____|\___/|_| \_\ \_/ |_____| \__,_|\__,_|\__|_| |_|
7
8ZMQ sockets: ZAP and CURVE helper functions
9Copyright (C) 2025 Paolo Bosetti
10*/
11
12#pragma once
13#ifdef _WIN32
14#ifndef NOMINMAX
15#define NOMINMAX
16#endif
17#ifndef WIN32_LEAN_AND_MEAN
18#define WIN32_LEAN_AND_MEAN
19#endif
20#include <winsock2.h>
21#endif
22#include <string>
23#include <vector>
24#include <filesystem>
25#include <fstream>
26#include <zmq.hpp>
27
28#include "zap_auth.hpp"
29
30using namespace std;
31namespace fs = std::filesystem;
32
33namespace Mads {
34
35enum class auth_verbose { off, on };
36
37class CurveAuth {
38public:
44 CurveAuth(zmq::context_t & context) : _authenticator(context) {}
45 ~CurveAuth() = default;
46
56 // Get some indication of what the authenticator is deciding
57 _authenticator.set_verbose(verbose == auth_verbose::on);
58 _authenticator.configure_domain("*");
59
60 // Whitelist our address; any other address will be rejected
61 for (const auto &ip : allowed_ips) {
62 _authenticator.allow(ip);
63 }
64 // Binds inproc://zeromq.zap.01 before returning, so no socket configured
65 // after this point can complete a handshake unauthenticated.
66 _authenticator.start();
67 }
68
78 void fetch_public_keys(fs::path const &key_dir) {
79 _key_dir = key_dir;
80 _client_keys.clear();
81 if (!fs::exists(key_dir) || !fs::is_directory(key_dir)) {
82 throw runtime_error("Key directory does not exist: " + key_dir.string());
83 }
84 for (const auto &entry : fs::directory_iterator(key_dir)) {
85 if (entry.path().extension() == ".pub") {
86 _client_keys.push_back(entry.path().stem().string());
87 }
88 }
89 if (_client_keys.empty()) {
90 throw runtime_error("No client public keys found in " + key_dir.string());
91 }
92 }
93
104 void setup_curve_server(zmq::socket_t &socket, string const &key_name) {
105 if (_key_dir.empty()) {
106 throw runtime_error("Key directory not set. Call fetch_public_keys() first.");
107 }
108 // We read the server certificates from files generated previously
109 CurveKeypair server_keypair;
110 string name = "";
111 try {
112 name = (_key_dir / (key_name + ".pub")).string();
113 ifstream server_pub_file(name);
114 if (!server_pub_file.is_open()) {
115 throw runtime_error(string("cannot open server public key file ") + name + " from " + _key_dir.string());
116 }
117 getline(server_pub_file, server_keypair.public_key);
118 server_pub_file.close();
119
120 name = (_key_dir / (key_name + ".key")).string();
121 ifstream server_key_file(name);
122 if (!server_key_file.is_open()) {
123 throw runtime_error(string("cannot open server secret key file ") + name + " from " + _key_dir.string());
124 }
125 getline(server_key_file, server_keypair.secret_key);
126 server_key_file.close();
127 } catch (const std::exception &e) {
128 throw runtime_error(e.what());
129 }
130 for (const auto &client_key_name : _client_keys) {
131 name = (_key_dir / (client_key_name + ".pub")).string();
132 ifstream client_pub_file(name);
133 if (!client_pub_file.is_open()) {
134 throw runtime_error("cannot open client public key file " + name + " from " + _key_dir.string());
135 }
136 string client_key;
137 getline(client_pub_file, client_key);
138 client_pub_file.close();
139 _authenticator.configure_curve(client_key);
140 }
141 socket.set(zmq::sockopt::routing_id, "IDENT");
142 socket.set(zmq::sockopt::curve_server, 1);
143 socket.set(zmq::sockopt::curve_secretkey, server_keypair.secret_key);
144 }
145
157 void setup_curve_client(zmq::socket_t &socket, string const &client_name, string const &server_name) {
158 // We load the certificates from files generated previously
159 try {
160 string name = (_key_dir / (client_name + ".pub")).string();
161 ifstream client_pub_file(name);
162 if (!client_pub_file.is_open()) {
163 throw runtime_error(string("cannot open client public key file ") + name + " from " + _key_dir.string());
164 }
165 getline(client_pub_file, _client_keypair.public_key);
166 client_pub_file.close();
167
168 name = (_key_dir / (server_name + ".pub")).string();
169 ifstream server_pub_file(name);
170 if (!server_pub_file.is_open()) {
171 throw runtime_error(string("cannot open server public key file ") + name + " from " + _key_dir.string());
172 }
173 getline(server_pub_file, _server_public_key);
174 server_pub_file.close();
175
176 name = (_key_dir / (client_name + ".key")).string();
177 ifstream client_key_file(name);
178 if (!client_key_file.is_open()) {
179 throw runtime_error(string("cannot open client private key file ") + name + " from " + _key_dir.string());
180 }
181 getline(client_key_file, _client_keypair.secret_key);
182 client_key_file.close();
183 } catch (const std::exception &e) {
184 throw runtime_error(e.what());
185 }
186
187 setup_curve_client(socket);
188 }
189
197 void setup_curve_client(zmq::socket_t &socket) {
198 if (_client_keypair.public_key.empty() || _client_keypair.secret_key.empty() || _server_public_key.empty()) {
199 throw runtime_error("Client or server keys not set. Call setup_curve_client() or set them individually first.");
200 }
201 socket.set(zmq::sockopt::curve_publickey, _client_keypair.public_key);
202 socket.set(zmq::sockopt::curve_secretkey, _client_keypair.secret_key);
203 socket.set(zmq::sockopt::curve_serverkey, _server_public_key);
204 }
205
206 void set_key_dir(fs::path const &key_dir) {
207 _key_dir = key_dir;
208 if (!fs::exists(_key_dir) || !fs::is_directory(_key_dir)) {
209 throw runtime_error("Key directory does not exist: " + _key_dir.string());
210 }
211 }
212
213 void set_client_public_key(string const &key) {
214 _client_keypair.public_key = key;
215 }
216
217 string client_public_key() const {
218 return _client_keypair.public_key;
219 }
220
221 void set_client_secret_key(string const &key) {
222 _client_keypair.secret_key = key;
223 }
224
225 string client_secret_key() const {
226 return _client_keypair.secret_key;
227 }
228
229 void set_server_public_key(string const &key) {
230 _server_public_key = key;
231 }
232
233 string server_public_key() const {
234 return _server_public_key;
235 }
236
237 // ZapAuth &get_authenticator() { return _authenticator; }
238
239 vector<string> allowed_ips{};
240private:
241 ZapAuth _authenticator;
242 vector<string> _client_keys{};
243 fs::path _key_dir;
244 CurveKeypair _client_keypair = {"", ""};
245 string _server_public_key = "";
246};
247
248
249
250} // namespace mads
string server_public_key() const
Definition curve.hpp:233
vector< string > allowed_ips
Definition curve.hpp:239
string client_public_key() const
Definition curve.hpp:217
void set_key_dir(fs::path const &key_dir)
Definition curve.hpp:206
void setup_auth(auth_verbose verbose=auth_verbose::off)
Sets up ZAP authentication with the specified verbosity.
Definition curve.hpp:55
void set_server_public_key(string const &key)
Definition curve.hpp:229
void set_client_public_key(string const &key)
Definition curve.hpp:213
void set_client_secret_key(string const &key)
Definition curve.hpp:221
string client_secret_key() const
Definition curve.hpp:225
CurveAuth(zmq::context_t &context)
Constructs a CurveAuth object for managing CURVE authentication.
Definition curve.hpp:44
void setup_curve_client(zmq::socket_t &socket)
Sets up CURVE security for a client socket using raw keys.
Definition curve.hpp:197
void fetch_public_keys(fs::path const &key_dir)
Fetches client public keys from the specified directory.
Definition curve.hpp:78
~CurveAuth()=default
void setup_curve_client(zmq::socket_t &socket, string const &client_name, string const &server_name)
Sets up CURVE security for a client socket.
Definition curve.hpp:157
void setup_curve_server(zmq::socket_t &socket, string const &key_name)
Sets up CURVE security for a server socket.
Definition curve.hpp:104
In-process ZAP (ZeroMQ Authentication Protocol, RFC 27) handler.
Definition zap_auth.hpp:54
void configure_curve(const std::string &z85_public_key)
Add a client public key to the CURVE allowlist.
void start()
Bind the ZAP endpoint and start servicing requests.
void set_verbose(bool verbose)
Log every authentication decision to stdout.
Definition zap_auth.hpp:92
void allow(const std::string &address)
Add an address to the whitelist.
void configure_domain(const std::string &domain)
Restrict the handler to one ZAP domain.
Definition agent.hpp:67
auth_verbose
Definition curve.hpp:35
A CURVE keypair, both keys Z85-encoded (40 characters each).
Definition zap_auth.hpp:166
std::string public_key
Definition zap_auth.hpp:167
std::string secret_key
Definition zap_auth.hpp:168