libzmq  master
ZeroMQ C++ Core Engine (LIBZMQ)
curve_client.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3 
4  This file is part of libzmq, the ZeroMQ core engine in C++.
5 
6  libzmq is free software; you can redistribute it and/or modify it under
7  the terms of the GNU Lesser General Public License (LGPL) as published
8  by the Free Software Foundation; either version 3 of the License, or
9  (at your option) any later version.
10 
11  As a special exception, the Contributors give you permission to link
12  this library with independent modules to produce an executable,
13  regardless of the license terms of these independent modules, and to
14  copy and distribute the resulting executable under terms of your choice,
15  provided that you also meet, for each linked independent module, the
16  terms and conditions of the license of that module. An independent
17  module is a module which is not derived from or based on this library.
18  If you modify this library, you must extend this exception to your
19  version of the library.
20 
21  libzmq is distributed in the hope that it will be useful, but WITHOUT
22  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24  License for more details.
25 
26  You should have received a copy of the GNU Lesser General Public License
27  along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29 
30 #include "precompiled.hpp"
31 #include "macros.hpp"
32 #include "platform.hpp"
33 
34 #ifdef ZMQ_HAVE_CURVE
35 
36 #ifdef ZMQ_HAVE_WINDOWS
37 #include "windows.hpp"
38 #endif
39 
40 #include "msg.hpp"
41 #include "session_base.hpp"
42 #include "err.hpp"
43 #include "curve_client.hpp"
44 #include "wire.hpp"
45 
46 zmq::curve_client_t::curve_client_t (const options_t &options_) :
47  mechanism_t (options_),
48  state (send_hello),
49  cn_nonce(1),
50  cn_peer_nonce(1)
51 {
52  int rc;
53  memcpy (public_key, options_.curve_public_key, crypto_box_PUBLICKEYBYTES);
54  memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
55  memcpy (server_key, options_.curve_server_key, crypto_box_PUBLICKEYBYTES);
56 
57  // Generate short-term key pair
58  rc = crypto_box_keypair (cn_public, cn_secret);
59  zmq_assert (rc == 0);
60 }
61 
62 zmq::curve_client_t::~curve_client_t ()
63 {
64 }
65 
66 int zmq::curve_client_t::next_handshake_command (msg_t *msg_)
67 {
68  int rc = 0;
69 
70  switch (state) {
71  case send_hello:
72  rc = produce_hello (msg_);
73  if (rc == 0)
74  state = expect_welcome;
75  break;
76  case send_initiate:
77  rc = produce_initiate (msg_);
78  if (rc == 0)
79  state = expect_ready;
80  break;
81  default:
82  errno = EAGAIN;
83  rc = -1;
84  }
85  return rc;
86 }
87 
88 int zmq::curve_client_t::process_handshake_command (msg_t *msg_)
89 {
90  const unsigned char *msg_data =
91  static_cast <unsigned char *> (msg_->data ());
92  const size_t msg_size = msg_->size ();
93 
94  int rc = 0;
95  if (msg_size >= 8 && !memcmp (msg_data, "\7WELCOME", 8))
96  rc = process_welcome (msg_data, msg_size);
97  else
98  if (msg_size >= 6 && !memcmp (msg_data, "\5READY", 6))
99  rc = process_ready (msg_data, msg_size);
100  else
101  if (msg_size >= 6 && !memcmp (msg_data, "\5ERROR", 6))
102  rc = process_error (msg_data, msg_size);
103  else {
104  errno = EPROTO;
105  rc = -1;
106  }
107 
108  if (rc == 0) {
109  rc = msg_->close ();
110  errno_assert (rc == 0);
111  rc = msg_->init ();
112  errno_assert (rc == 0);
113  }
114 
115  return rc;
116 }
117 
118 int zmq::curve_client_t::encode (msg_t *msg_)
119 {
120  zmq_assert (state == connected);
121 
122  uint8_t flags = 0;
123  if (msg_->flags () & msg_t::more)
124  flags |= 0x01;
125  if (msg_->flags () & msg_t::command)
126  flags |= 0x02;
127 
128  uint8_t message_nonce [crypto_box_NONCEBYTES];
129  memcpy (message_nonce, "CurveZMQMESSAGEC", 16);
130  put_uint64 (message_nonce + 16, cn_nonce);
131 
132  const size_t mlen = crypto_box_ZEROBYTES + 1 + msg_->size ();
133 
134  uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (mlen));
135  alloc_assert (message_plaintext);
136 
137  memset (message_plaintext, 0, crypto_box_ZEROBYTES);
138  message_plaintext [crypto_box_ZEROBYTES] = flags;
139  memcpy (message_plaintext + crypto_box_ZEROBYTES + 1,
140  msg_->data (), msg_->size ());
141 
142  uint8_t *message_box = static_cast <uint8_t *> (malloc (mlen));
143  alloc_assert (message_box);
144 
145  int rc = crypto_box_afternm (message_box, message_plaintext,
146  mlen, message_nonce, cn_precom);
147  zmq_assert (rc == 0);
148 
149  rc = msg_->close ();
150  zmq_assert (rc == 0);
151 
152  rc = msg_->init_size (16 + mlen - crypto_box_BOXZEROBYTES);
153  zmq_assert (rc == 0);
154 
155  uint8_t *message = static_cast <uint8_t *> (msg_->data ());
156 
157  memcpy (message, "\x07MESSAGE", 8);
158  memcpy (message + 8, message_nonce + 16, 8);
159  memcpy (message + 16, message_box + crypto_box_BOXZEROBYTES,
160  mlen - crypto_box_BOXZEROBYTES);
161 
162  free (message_plaintext);
163  free (message_box);
164 
165  cn_nonce++;
166 
167  return 0;
168 }
169 
170 int zmq::curve_client_t::decode (msg_t *msg_)
171 {
172  zmq_assert (state == connected);
173 
174  if (msg_->size () < 33) {
175  errno = EPROTO;
176  return -1;
177  }
178 
179  const uint8_t *message = static_cast <uint8_t *> (msg_->data ());
180  if (memcmp (message, "\x07MESSAGE", 8)) {
181  errno = EPROTO;
182  return -1;
183  }
184 
185  uint8_t message_nonce [crypto_box_NONCEBYTES];
186  memcpy (message_nonce, "CurveZMQMESSAGES", 16);
187  memcpy (message_nonce + 16, message + 8, 8);
188  uint64_t nonce = get_uint64(message + 8);
189  if (nonce <= cn_peer_nonce) {
190  errno = EPROTO;
191  return -1;
192  }
193  cn_peer_nonce = nonce;
194 
195  const size_t clen = crypto_box_BOXZEROBYTES + (msg_->size () - 16);
196 
197  uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (clen));
198  alloc_assert (message_plaintext);
199 
200  uint8_t *message_box = static_cast <uint8_t *> (malloc (clen));
201  alloc_assert (message_box);
202 
203  memset (message_box, 0, crypto_box_BOXZEROBYTES);
204  memcpy (message_box + crypto_box_BOXZEROBYTES,
205  message + 16, msg_->size () - 16);
206 
207  int rc = crypto_box_open_afternm (message_plaintext, message_box,
208  clen, message_nonce, cn_precom);
209  if (rc == 0) {
210  rc = msg_->close ();
211  zmq_assert (rc == 0);
212 
213  rc = msg_->init_size (clen - 1 - crypto_box_ZEROBYTES);
214  zmq_assert (rc == 0);
215 
216  const uint8_t flags = message_plaintext [crypto_box_ZEROBYTES];
217  if (flags & 0x01)
218  msg_->set_flags (msg_t::more);
219  if (flags & 0x02)
220  msg_->set_flags (msg_t::command);
221 
222  memcpy (msg_->data (),
223  message_plaintext + crypto_box_ZEROBYTES + 1,
224  msg_->size ());
225  }
226  else
227  errno = EPROTO;
228 
229  free (message_plaintext);
230  free (message_box);
231 
232  return rc;
233 }
234 
235 zmq::mechanism_t::status_t zmq::curve_client_t::status () const
236 {
237  if (state == connected)
238  return mechanism_t::ready;
239  else
240  if (state == error_received)
241  return mechanism_t::error;
242  else
244 }
245 
246 int zmq::curve_client_t::produce_hello (msg_t *msg_)
247 {
248  uint8_t hello_nonce [crypto_box_NONCEBYTES];
249  uint8_t hello_plaintext [crypto_box_ZEROBYTES + 64];
250  uint8_t hello_box [crypto_box_BOXZEROBYTES + 80];
251 
252  // Prepare the full nonce
253  memcpy (hello_nonce, "CurveZMQHELLO---", 16);
254  put_uint64 (hello_nonce + 16, cn_nonce);
255 
256  // Create Box [64 * %x0](C'->S)
257  memset (hello_plaintext, 0, sizeof hello_plaintext);
258 
259  int rc = crypto_box (hello_box, hello_plaintext,
260  sizeof hello_plaintext,
261  hello_nonce, server_key, cn_secret);
262  if (rc == -1)
263  return -1;
264 
265  rc = msg_->init_size (200);
266  errno_assert (rc == 0);
267  uint8_t *hello = static_cast <uint8_t *> (msg_->data ());
268 
269  memcpy (hello, "\x05HELLO", 6);
270  // CurveZMQ major and minor version numbers
271  memcpy (hello + 6, "\1\0", 2);
272  // Anti-amplification padding
273  memset (hello + 8, 0, 72);
274  // Client public connection key
275  memcpy (hello + 80, cn_public, crypto_box_PUBLICKEYBYTES);
276  // Short nonce, prefixed by "CurveZMQHELLO---"
277  memcpy (hello + 112, hello_nonce + 16, 8);
278  // Signature, Box [64 * %x0](C'->S)
279  memcpy (hello + 120, hello_box + crypto_box_BOXZEROBYTES, 80);
280 
281  cn_nonce++;
282 
283  return 0;
284 }
285 
286 int zmq::curve_client_t::process_welcome (
287  const uint8_t *msg_data, size_t msg_size)
288 {
289  if (msg_size != 168) {
290  errno = EPROTO;
291  return -1;
292  }
293 
294  uint8_t welcome_nonce [crypto_box_NONCEBYTES];
295  uint8_t welcome_plaintext [crypto_box_ZEROBYTES + 128];
296  uint8_t welcome_box [crypto_box_BOXZEROBYTES + 144];
297 
298  // Open Box [S' + cookie](C'->S)
299  memset (welcome_box, 0, crypto_box_BOXZEROBYTES);
300  memcpy (welcome_box + crypto_box_BOXZEROBYTES, msg_data + 24, 144);
301 
302  memcpy (welcome_nonce, "WELCOME-", 8);
303  memcpy (welcome_nonce + 8, msg_data + 8, 16);
304 
305  int rc = crypto_box_open (welcome_plaintext, welcome_box,
306  sizeof welcome_box,
307  welcome_nonce, server_key, cn_secret);
308  if (rc != 0) {
309  errno = EPROTO;
310  return -1;
311  }
312 
313  memcpy (cn_server, welcome_plaintext + crypto_box_ZEROBYTES, 32);
314  memcpy (cn_cookie, welcome_plaintext + crypto_box_ZEROBYTES + 32, 16 + 80);
315 
316  // Message independent precomputation
317  rc = crypto_box_beforenm (cn_precom, cn_server, cn_secret);
318  zmq_assert (rc == 0);
319 
320  state = send_initiate;
321 
322  return 0;
323 }
324 
325 int zmq::curve_client_t::produce_initiate (msg_t *msg_)
326 {
327  uint8_t vouch_nonce [crypto_box_NONCEBYTES];
328  uint8_t vouch_plaintext [crypto_box_ZEROBYTES + 64];
329  uint8_t vouch_box [crypto_box_BOXZEROBYTES + 80];
330 
331  // Create vouch = Box [C',S](C->S')
332  memset (vouch_plaintext, 0, crypto_box_ZEROBYTES);
333  memcpy (vouch_plaintext + crypto_box_ZEROBYTES, cn_public, 32);
334  memcpy (vouch_plaintext + crypto_box_ZEROBYTES + 32, server_key, 32);
335 
336  memcpy (vouch_nonce, "VOUCH---", 8);
337  randombytes (vouch_nonce + 8, 16);
338 
339  int rc = crypto_box (vouch_box, vouch_plaintext,
340  sizeof vouch_plaintext,
341  vouch_nonce, cn_server, secret_key);
342  if (rc == -1)
343  return -1;
344 
345  // Assume here that metadata is limited to 256 bytes
346  uint8_t initiate_nonce [crypto_box_NONCEBYTES];
347  uint8_t initiate_plaintext [crypto_box_ZEROBYTES + 128 + 256];
348  uint8_t initiate_box [crypto_box_BOXZEROBYTES + 144 + 256];
349 
350  // Create Box [C + vouch + metadata](C'->S')
351  memset (initiate_plaintext, 0, crypto_box_ZEROBYTES);
352  memcpy (initiate_plaintext + crypto_box_ZEROBYTES,
353  public_key, 32);
354  memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 32,
355  vouch_nonce + 8, 16);
356  memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 48,
357  vouch_box + crypto_box_BOXZEROBYTES, 80);
358 
359  // Metadata starts after vouch
360  uint8_t *ptr = initiate_plaintext + crypto_box_ZEROBYTES + 128;
361 
362  // Add socket type property
363  const char *socket_type = socket_type_string (options.type);
364  ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type));
365 
366  // Add identity property
367  if (options.type == ZMQ_REQ
368  || options.type == ZMQ_DEALER
369  || options.type == ZMQ_ROUTER)
370  ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
371 
372  const size_t mlen = ptr - initiate_plaintext;
373 
374  memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
375  put_uint64 (initiate_nonce + 16, cn_nonce);
376 
377  rc = crypto_box (initiate_box, initiate_plaintext,
378  mlen, initiate_nonce, cn_server, cn_secret);
379  if (rc == -1)
380  return -1;
381 
382  rc = msg_->init_size (113 + mlen - crypto_box_BOXZEROBYTES);
383  errno_assert (rc == 0);
384 
385  uint8_t *initiate = static_cast <uint8_t *> (msg_->data ());
386 
387  memcpy (initiate, "\x08INITIATE", 9);
388  // Cookie provided by the server in the WELCOME command
389  memcpy (initiate + 9, cn_cookie, 96);
390  // Short nonce, prefixed by "CurveZMQINITIATE"
391  memcpy (initiate + 105, initiate_nonce + 16, 8);
392  // Box [C + vouch + metadata](C'->S')
393  memcpy (initiate + 113, initiate_box + crypto_box_BOXZEROBYTES,
394  mlen - crypto_box_BOXZEROBYTES);
395  cn_nonce++;
396 
397  return 0;
398 }
399 
400 int zmq::curve_client_t::process_ready (
401  const uint8_t *msg_data, size_t msg_size)
402 {
403  if (msg_size < 30) {
404  errno = EPROTO;
405  return -1;
406  }
407 
408  const size_t clen = (msg_size - 14) + crypto_box_BOXZEROBYTES;
409 
410  uint8_t ready_nonce [crypto_box_NONCEBYTES];
411  uint8_t ready_plaintext [crypto_box_ZEROBYTES + 256];
412  uint8_t ready_box [crypto_box_BOXZEROBYTES + 16 + 256];
413 
414  memset (ready_box, 0, crypto_box_BOXZEROBYTES);
415  memcpy (ready_box + crypto_box_BOXZEROBYTES,
416  msg_data + 14, clen - crypto_box_BOXZEROBYTES);
417 
418  memcpy (ready_nonce, "CurveZMQREADY---", 16);
419  memcpy (ready_nonce + 16, msg_data + 6, 8);
420  cn_peer_nonce = get_uint64(msg_data + 6);
421 
422  int rc = crypto_box_open_afternm (ready_plaintext, ready_box,
423  clen, ready_nonce, cn_precom);
424 
425  if (rc != 0) {
426  errno = EPROTO;
427  return -1;
428  }
429 
430  rc = parse_metadata (ready_plaintext + crypto_box_ZEROBYTES,
431  clen - crypto_box_ZEROBYTES);
432  if (rc == 0)
433  state = connected;
434 
435  return rc;
436 }
437 
438 int zmq::curve_client_t::process_error (
439  const uint8_t *msg_data, size_t msg_size)
440 {
441  if (state != expect_welcome && state != expect_ready) {
442  errno = EPROTO;
443  return -1;
444  }
445  if (msg_size < 7) {
446  errno = EPROTO;
447  return -1;
448  }
449  const size_t error_reason_len = static_cast <size_t> (msg_data [6]);
450  if (error_reason_len > msg_size - 7) {
451  errno = EPROTO;
452  return -1;
453  }
454  state = error_received;
455  return 0;
456 }
457 
458 #endif
#define ZMQ_DEALER
Definition: zmq.h:251
#define zmq_assert(x)
Definition: err.hpp:119
#define ZMQ_ROUTER
Definition: zmq.h:252
#define ZMQ_REQ
Definition: zmq.h:249
uint64_t get_uint64(const unsigned char *buffer_)
Definition: wire.hpp:93
#define EPROTO
Definition: err.hpp:58
void put_uint64(unsigned char *buffer_, uint64_t value)
Definition: wire.hpp:81
#define alloc_assert(x)
Definition: err.hpp:159
#define errno_assert(x)
Definition: err.hpp:129