curve_server.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MPL-2.0 */
2 
3 #include "precompiled.hpp"
4 #include "macros.hpp"
5 
6 #ifdef ZMQ_HAVE_CURVE
7 
8 #include "msg.hpp"
9 #include "session_base.hpp"
10 #include "err.hpp"
11 #include "curve_server.hpp"
12 #include "wire.hpp"
13 #include "secure_allocator.hpp"
14 
15 zmq::curve_server_t::curve_server_t (session_base_t *session_,
16  const std::string &peer_address_,
17  const options_t &options_,
18  const bool downgrade_sub_) :
19  mechanism_base_t (session_, options_),
20  zap_client_common_handshake_t (
21  session_, peer_address_, options_, sending_ready),
22  curve_mechanism_base_t (session_,
23  options_,
24  "CurveZMQMESSAGES",
25  "CurveZMQMESSAGEC",
26  downgrade_sub_)
27 {
28  int rc;
29  // Fetch our secret key from socket options
30  memcpy (_secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
31 
32  // Generate short-term key pair
33  memset (_cn_secret, 0, crypto_box_SECRETKEYBYTES);
34  memset (_cn_public, 0, crypto_box_PUBLICKEYBYTES);
35  rc = crypto_box_keypair (_cn_public, _cn_secret);
36  zmq_assert (rc == 0);
37 }
38 
39 zmq::curve_server_t::~curve_server_t ()
40 {
41 }
42 
43 int zmq::curve_server_t::next_handshake_command (msg_t *msg_)
44 {
45  int rc = 0;
46 
47  switch (state) {
48  case sending_welcome:
49  rc = produce_welcome (msg_);
50  if (rc == 0)
51  state = waiting_for_initiate;
52  break;
53  case sending_ready:
54  rc = produce_ready (msg_);
55  if (rc == 0)
56  state = ready;
57  break;
58  case sending_error:
59  rc = produce_error (msg_);
60  if (rc == 0)
61  state = error_sent;
62  break;
63  default:
64  errno = EAGAIN;
65  rc = -1;
66  break;
67  }
68  return rc;
69 }
70 
71 int zmq::curve_server_t::process_handshake_command (msg_t *msg_)
72 {
73  int rc = 0;
74 
75  switch (state) {
76  case waiting_for_hello:
77  rc = process_hello (msg_);
78  break;
79  case waiting_for_initiate:
80  rc = process_initiate (msg_);
81  break;
82  default:
83  // TODO I think this is not a case reachable with a misbehaving
84  // client. It is not an "invalid handshake command", but would be
85  // trying to process a handshake command in an invalid state,
86  // which is purely under control of this peer.
87  // Therefore, it should be changed to zmq_assert (false);
88 
89  // CURVE I: invalid handshake command
90  session->get_socket ()->event_handshake_failed_protocol (
91  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED);
92  errno = EPROTO;
93  rc = -1;
94  break;
95  }
96  if (rc == 0) {
97  rc = msg_->close ();
98  errno_assert (rc == 0);
99  rc = msg_->init ();
100  errno_assert (rc == 0);
101  }
102  return rc;
103 }
104 
105 int zmq::curve_server_t::encode (msg_t *msg_)
106 {
107  zmq_assert (state == ready);
108  return curve_mechanism_base_t::encode (msg_);
109 }
110 
111 int zmq::curve_server_t::decode (msg_t *msg_)
112 {
113  zmq_assert (state == ready);
114  return curve_mechanism_base_t::decode (msg_);
115 }
116 
117 int zmq::curve_server_t::process_hello (msg_t *msg_)
118 {
119  int rc = check_basic_command_structure (msg_);
120  if (rc == -1)
121  return -1;
122 
123  const size_t size = msg_->size ();
124  const uint8_t *const hello = static_cast<uint8_t *> (msg_->data ());
125 
126  if (size < 6 || memcmp (hello, "\x05HELLO", 6)) {
127  session->get_socket ()->event_handshake_failed_protocol (
128  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
129  errno = EPROTO;
130  return -1;
131  }
132 
133  if (size != 200) {
134  session->get_socket ()->event_handshake_failed_protocol (
135  session->get_endpoint (),
137  errno = EPROTO;
138  return -1;
139  }
140 
141  const uint8_t major = hello[6];
142  const uint8_t minor = hello[7];
143 
144  if (major != 1 || minor != 0) {
145  // CURVE I: client HELLO has unknown version number
146  session->get_socket ()->event_handshake_failed_protocol (
147  session->get_endpoint (),
149  errno = EPROTO;
150  return -1;
151  }
152 
153  // Save client's short-term public key (C')
154  memcpy (_cn_client, hello + 80, 32);
155 
156  uint8_t hello_nonce[crypto_box_NONCEBYTES];
157  std::vector<uint8_t, secure_allocator_t<uint8_t> > hello_plaintext (
158  crypto_box_ZEROBYTES + 64);
159  uint8_t hello_box[crypto_box_BOXZEROBYTES + 80];
160 
161  memcpy (hello_nonce, "CurveZMQHELLO---", 16);
162  memcpy (hello_nonce + 16, hello + 112, 8);
163  set_peer_nonce (get_uint64 (hello + 112));
164 
165  memset (hello_box, 0, crypto_box_BOXZEROBYTES);
166  memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80);
167 
168  // Open Box [64 * %x0](C'->S)
169  rc = crypto_box_open (&hello_plaintext[0], hello_box, sizeof hello_box,
170  hello_nonce, _cn_client, _secret_key);
171  if (rc != 0) {
172  // CURVE I: cannot open client HELLO -- wrong server key?
173  session->get_socket ()->event_handshake_failed_protocol (
174  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
175  errno = EPROTO;
176  return -1;
177  }
178 
179  state = sending_welcome;
180  return rc;
181 }
182 
183 int zmq::curve_server_t::produce_welcome (msg_t *msg_)
184 {
185  uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES];
186  std::vector<uint8_t, secure_allocator_t<uint8_t> > cookie_plaintext (
187  crypto_secretbox_ZEROBYTES + 64);
188  uint8_t cookie_ciphertext[crypto_secretbox_BOXZEROBYTES + 80];
189 
190  // Create full nonce for encryption
191  // 8-byte prefix plus 16-byte random nonce
192  memset (cookie_nonce, 0, crypto_secretbox_NONCEBYTES);
193  memcpy (cookie_nonce, "COOKIE--", 8);
194  randombytes (cookie_nonce + 8, 16);
195 
196  // Generate cookie = Box [C' + s'](t)
197  std::fill (cookie_plaintext.begin (),
198  cookie_plaintext.begin () + crypto_secretbox_ZEROBYTES, 0);
199  memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES], _cn_client, 32);
200  memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES + 32], _cn_secret, 32);
201 
202  // Generate fresh cookie key
203  memset (_cookie_key, 0, crypto_secretbox_KEYBYTES);
204  randombytes (_cookie_key, crypto_secretbox_KEYBYTES);
205 
206  // Encrypt using symmetric cookie key
207  int rc =
208  crypto_secretbox (cookie_ciphertext, &cookie_plaintext[0],
209  cookie_plaintext.size (), cookie_nonce, _cookie_key);
210  zmq_assert (rc == 0);
211 
212  uint8_t welcome_nonce[crypto_box_NONCEBYTES];
213  std::vector<uint8_t, secure_allocator_t<uint8_t> > welcome_plaintext (
214  crypto_box_ZEROBYTES + 128);
215  uint8_t welcome_ciphertext[crypto_box_BOXZEROBYTES + 144];
216 
217  // Create full nonce for encryption
218  // 8-byte prefix plus 16-byte random nonce
219  memset (welcome_nonce, 0, crypto_box_NONCEBYTES);
220  memcpy (welcome_nonce, "WELCOME-", 8);
221  randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8);
222 
223  // Create 144-byte Box [S' + cookie](S->C')
224  std::fill (welcome_plaintext.begin (),
225  welcome_plaintext.begin () + crypto_box_ZEROBYTES, 0);
226  memcpy (&welcome_plaintext[crypto_box_ZEROBYTES], _cn_public, 32);
227  memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 32], cookie_nonce + 8,
228  16);
229  memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 48],
230  cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80);
231 
232  rc = crypto_box (welcome_ciphertext, &welcome_plaintext[0],
233  welcome_plaintext.size (), welcome_nonce, _cn_client,
234  _secret_key);
235 
236  // TODO I think we should change this back to zmq_assert (rc == 0);
237  // as it was before https://github.com/zeromq/libzmq/pull/1832
238  // The reason given there was that secret_key might be 0ed.
239  // But if it were, we would never get this far, since we could
240  // not have opened the client's hello box with a 0ed key.
241 
242  if (rc == -1)
243  return -1;
244 
245  rc = msg_->init_size (168);
246  errno_assert (rc == 0);
247 
248  uint8_t *const welcome = static_cast<uint8_t *> (msg_->data ());
249  memcpy (welcome, "\x07WELCOME", 8);
250  memcpy (welcome + 8, welcome_nonce + 8, 16);
251  memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144);
252 
253  return 0;
254 }
255 
256 int zmq::curve_server_t::process_initiate (msg_t *msg_)
257 {
258  int rc = check_basic_command_structure (msg_);
259  if (rc == -1)
260  return -1;
261 
262  const size_t size = msg_->size ();
263  const uint8_t *initiate = static_cast<uint8_t *> (msg_->data ());
264 
265  if (size < 9 || memcmp (initiate, "\x08INITIATE", 9)) {
266  session->get_socket ()->event_handshake_failed_protocol (
267  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
268  errno = EPROTO;
269  return -1;
270  }
271 
272  if (size < 257) {
273  session->get_socket ()->event_handshake_failed_protocol (
274  session->get_endpoint (),
276  errno = EPROTO;
277  return -1;
278  }
279 
280  uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES];
281  uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64];
282  uint8_t cookie_box[crypto_secretbox_BOXZEROBYTES + 80];
283 
284  // Open Box [C' + s'](t)
285  memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES);
286  memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80);
287 
288  memcpy (cookie_nonce, "COOKIE--", 8);
289  memcpy (cookie_nonce + 8, initiate + 9, 16);
290 
291  rc = crypto_secretbox_open (cookie_plaintext, cookie_box, sizeof cookie_box,
292  cookie_nonce, _cookie_key);
293  if (rc != 0) {
294  // CURVE I: cannot open client INITIATE cookie
295  session->get_socket ()->event_handshake_failed_protocol (
296  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
297  errno = EPROTO;
298  return -1;
299  }
300 
301  // Check cookie plain text is as expected [C' + s']
302  if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32)
303  || memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32,
304  _cn_secret, 32)) {
305  // TODO this case is very hard to test, as it would require a modified
306  // client that knows the server's secret temporary cookie key
307 
308  // CURVE I: client INITIATE cookie is not valid
309  session->get_socket ()->event_handshake_failed_protocol (
310  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
311  errno = EPROTO;
312  return -1;
313  }
314 
315  const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES;
316 
317  uint8_t initiate_nonce[crypto_box_NONCEBYTES];
318  std::vector<uint8_t, secure_allocator_t<uint8_t> > initiate_plaintext (
319  crypto_box_ZEROBYTES + clen);
320  std::vector<uint8_t> initiate_box (crypto_box_BOXZEROBYTES + clen);
321 
322  // Open Box [C + vouch + metadata](C'->S')
323  std::fill (initiate_box.begin (),
324  initiate_box.begin () + crypto_box_BOXZEROBYTES, 0);
325  memcpy (&initiate_box[crypto_box_BOXZEROBYTES], initiate + 113,
326  clen - crypto_box_BOXZEROBYTES);
327 
328  memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
329  memcpy (initiate_nonce + 16, initiate + 105, 8);
330  set_peer_nonce (get_uint64 (initiate + 105));
331 
332  const uint8_t *client_key = &initiate_plaintext[crypto_box_ZEROBYTES];
333 
334  rc = crypto_box_open (&initiate_plaintext[0], &initiate_box[0], clen,
335  initiate_nonce, _cn_client, _cn_secret);
336  if (rc != 0) {
337  // CURVE I: cannot open client INITIATE
338  session->get_socket ()->event_handshake_failed_protocol (
339  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
340  errno = EPROTO;
341  return -1;
342  }
343 
344  uint8_t vouch_nonce[crypto_box_NONCEBYTES];
345  std::vector<uint8_t, secure_allocator_t<uint8_t> > vouch_plaintext (
346  crypto_box_ZEROBYTES + 64);
347  uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80];
348 
349  // Open Box Box [C',S](C->S') and check contents
350  memset (vouch_box, 0, crypto_box_BOXZEROBYTES);
351  memcpy (vouch_box + crypto_box_BOXZEROBYTES,
352  &initiate_plaintext[crypto_box_ZEROBYTES + 48], 80);
353 
354  memset (vouch_nonce, 0, crypto_box_NONCEBYTES);
355  memcpy (vouch_nonce, "VOUCH---", 8);
356  memcpy (vouch_nonce + 8, &initiate_plaintext[crypto_box_ZEROBYTES + 32],
357  16);
358 
359  rc = crypto_box_open (&vouch_plaintext[0], vouch_box, sizeof vouch_box,
360  vouch_nonce, client_key, _cn_secret);
361  if (rc != 0) {
362  // CURVE I: cannot open client INITIATE vouch
363  session->get_socket ()->event_handshake_failed_protocol (
364  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
365  errno = EPROTO;
366  return -1;
367  }
368 
369  // What we decrypted must be the client's short-term public key
370  if (memcmp (&vouch_plaintext[crypto_box_ZEROBYTES], _cn_client, 32)) {
371  // TODO this case is very hard to test, as it would require a modified
372  // client that knows the server's secret short-term key
373 
374  // CURVE I: invalid handshake from client (public key)
375  session->get_socket ()->event_handshake_failed_protocol (
376  session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE);
377  errno = EPROTO;
378  return -1;
379  }
380 
381  // Precompute connection secret from client key
382  rc = crypto_box_beforenm (get_writable_precom_buffer (), _cn_client,
383  _cn_secret);
384  zmq_assert (rc == 0);
385 
386  // Given this is a backward-incompatible change, it's behind a socket
387  // option disabled by default.
388  if (zap_required () || !options.zap_enforce_domain) {
389  // Use ZAP protocol (RFC 27) to authenticate the user.
390  rc = session->zap_connect ();
391  if (rc == 0) {
392  send_zap_request (client_key);
393  state = waiting_for_zap_reply;
394 
395  // TODO actually, it is quite unlikely that we can read the ZAP
396  // reply already, but removing this has some strange side-effect
397  // (probably because the pipe's in_active flag is true until a read
398  // is attempted)
399  if (-1 == receive_and_process_zap_reply ())
400  return -1;
401  } else if (!options.zap_enforce_domain) {
402  // This supports the Stonehouse pattern (encryption without
403  // authentication) in legacy mode (domain set but no handler).
404  state = sending_ready;
405  } else {
406  session->get_socket ()->event_handshake_failed_no_detail (
407  session->get_endpoint (), EFAULT);
408  return -1;
409  }
410  } else {
411  // This supports the Stonehouse pattern (encryption without authentication).
412  state = sending_ready;
413  }
414 
415  return parse_metadata (&initiate_plaintext[crypto_box_ZEROBYTES + 128],
416  clen - crypto_box_ZEROBYTES - 128);
417 }
418 
419 int zmq::curve_server_t::produce_ready (msg_t *msg_)
420 {
421  const size_t metadata_length = basic_properties_len ();
422  uint8_t ready_nonce[crypto_box_NONCEBYTES];
423 
424  std::vector<uint8_t, secure_allocator_t<uint8_t> > ready_plaintext (
425  crypto_box_ZEROBYTES + metadata_length);
426 
427  // Create Box [metadata](S'->C')
428  std::fill (ready_plaintext.begin (),
429  ready_plaintext.begin () + crypto_box_ZEROBYTES, 0);
430  uint8_t *ptr = &ready_plaintext[crypto_box_ZEROBYTES];
431 
432  ptr += add_basic_properties (ptr, metadata_length);
433  const size_t mlen = ptr - &ready_plaintext[0];
434 
435  memcpy (ready_nonce, "CurveZMQREADY---", 16);
436  put_uint64 (ready_nonce + 16, get_and_inc_nonce ());
437 
438  std::vector<uint8_t> ready_box (crypto_box_BOXZEROBYTES + 16
439  + metadata_length);
440 
441  int rc = crypto_box_afternm (&ready_box[0], &ready_plaintext[0], mlen,
442  ready_nonce, get_precom_buffer ());
443  zmq_assert (rc == 0);
444 
445  rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES);
446  errno_assert (rc == 0);
447 
448  uint8_t *ready = static_cast<uint8_t *> (msg_->data ());
449 
450  memcpy (ready, "\x05READY", 6);
451  // Short nonce, prefixed by "CurveZMQREADY---"
452  memcpy (ready + 6, ready_nonce + 16, 8);
453  // Box [metadata](S'->C')
454  memcpy (ready + 14, &ready_box[crypto_box_BOXZEROBYTES],
455  mlen - crypto_box_BOXZEROBYTES);
456 
457  return 0;
458 }
459 
460 int zmq::curve_server_t::produce_error (msg_t *msg_) const
461 {
462  const size_t expected_status_code_length = 3;
463  zmq_assert (status_code.length () == 3);
464  const int rc = msg_->init_size (6 + 1 + expected_status_code_length);
465  zmq_assert (rc == 0);
466  char *msg_data = static_cast<char *> (msg_->data ());
467  memcpy (msg_data, "\5ERROR", 6);
468  msg_data[6] = expected_status_code_length;
469  memcpy (msg_data + 7, status_code.c_str (), expected_status_code_length);
470  return 0;
471 }
472 
473 void zmq::curve_server_t::send_zap_request (const uint8_t *key_)
474 {
475  zap_client_t::send_zap_request ("CURVE", 5, key_,
476  crypto_box_PUBLICKEYBYTES);
477 }
478 
479 #endif
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE
#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE
Definition: zmq.h:431
ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND
#define ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND
Definition: zmq.h:425
options
Message * options
Definition: src/google/protobuf/descriptor.cc:3119
ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC
#define ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC
Definition: zmq.h:437
EAGAIN
#define EAGAIN
Definition: errno.hpp:14
ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE
#define ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE
Definition: zmq.h:427
precompiled.hpp
zmq_assert
#define zmq_assert(x)
Definition: err.hpp:102
string
GLsizei const GLchar *const * string
Definition: glcorearb.h:3083
errno
int errno
wire.hpp
EPROTO
#define EPROTO
Definition: err.hpp:26
errno_assert
#define errno_assert(x)
Definition: err.hpp:113
macros.hpp
ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED
#define ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED
Definition: zmq.h:424
major
int major
Definition: gl3w.c:93
curve_server.hpp
secure_allocator.hpp
minor
int minor
Definition: gl3w.c:93
msg.hpp
zmq::put_uint64
void put_uint64(unsigned char *buffer_, uint64_t value_)
Definition: wire.hpp:51
size
GLsizeiptr size
Definition: glcorearb.h:2943
zmq::get_uint64
uint64_t get_uint64(const unsigned char *buffer_)
Definition: wire.hpp:63
err.hpp
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO
#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO
Definition: zmq.h:430
session_base.hpp
EFAULT
#define EFAULT
Definition: errno.hpp:17
options_
DebugStringOptions options_
Definition: src/google/protobuf/descriptor.cc:2410


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:49