Line data Source code
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 45 : zmq::curve_client_t::curve_client_t (const options_t &options_) :
47 : mechanism_t (options_),
48 : state (send_hello),
49 : cn_nonce(1),
50 45 : cn_peer_nonce(1)
51 : {
52 : int rc;
53 45 : memcpy (public_key, options_.curve_public_key, crypto_box_PUBLICKEYBYTES);
54 45 : memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
55 45 : memcpy (server_key, options_.curve_server_key, crypto_box_PUBLICKEYBYTES);
56 :
57 : // Generate short-term key pair
58 45 : rc = crypto_box_keypair (cn_public, cn_secret);
59 45 : zmq_assert (rc == 0);
60 45 : }
61 :
62 45 : zmq::curve_client_t::~curve_client_t ()
63 : {
64 45 : }
65 :
66 234 : int zmq::curve_client_t::next_handshake_command (msg_t *msg_)
67 : {
68 234 : int rc = 0;
69 :
70 234 : switch (state) {
71 : case send_hello:
72 45 : rc = produce_hello (msg_);
73 45 : if (rc == 0)
74 45 : state = expect_welcome;
75 : break;
76 : case send_initiate:
77 33 : rc = produce_initiate (msg_);
78 33 : if (rc == 0)
79 33 : state = expect_ready;
80 : break;
81 : default:
82 156 : errno = EAGAIN;
83 156 : rc = -1;
84 : }
85 234 : return rc;
86 : }
87 :
88 42 : int zmq::curve_client_t::process_handshake_command (msg_t *msg_)
89 : {
90 : const unsigned char *msg_data =
91 42 : static_cast <unsigned char *> (msg_->data ());
92 42 : const size_t msg_size = msg_->size ();
93 :
94 42 : int rc = 0;
95 42 : if (msg_size >= 8 && !memcmp (msg_data, "\7WELCOME", 8))
96 33 : rc = process_welcome (msg_data, msg_size);
97 : else
98 9 : if (msg_size >= 6 && !memcmp (msg_data, "\5READY", 6))
99 6 : rc = process_ready (msg_data, msg_size);
100 : else
101 3 : if (msg_size >= 6 && !memcmp (msg_data, "\5ERROR", 6))
102 3 : rc = process_error (msg_data, msg_size);
103 : else {
104 0 : errno = EPROTO;
105 0 : rc = -1;
106 : }
107 :
108 42 : if (rc == 0) {
109 39 : rc = msg_->close ();
110 39 : errno_assert (rc == 0);
111 39 : rc = msg_->init ();
112 39 : errno_assert (rc == 0);
113 : }
114 :
115 42 : return rc;
116 : }
117 :
118 57 : int zmq::curve_client_t::encode (msg_t *msg_)
119 : {
120 57 : zmq_assert (state == connected);
121 :
122 57 : uint8_t flags = 0;
123 57 : if (msg_->flags () & msg_t::more)
124 3 : flags |= 0x01;
125 57 : if (msg_->flags () & msg_t::command)
126 51 : flags |= 0x02;
127 :
128 : uint8_t message_nonce [crypto_box_NONCEBYTES];
129 : memcpy (message_nonce, "CurveZMQMESSAGEC", 16);
130 57 : put_uint64 (message_nonce + 16, cn_nonce);
131 :
132 57 : const size_t mlen = crypto_box_ZEROBYTES + 1 + msg_->size ();
133 :
134 57 : uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (mlen));
135 57 : alloc_assert (message_plaintext);
136 :
137 : memset (message_plaintext, 0, crypto_box_ZEROBYTES);
138 57 : message_plaintext [crypto_box_ZEROBYTES] = flags;
139 57 : memcpy (message_plaintext + crypto_box_ZEROBYTES + 1,
140 57 : msg_->data (), msg_->size ());
141 :
142 57 : uint8_t *message_box = static_cast <uint8_t *> (malloc (mlen));
143 57 : alloc_assert (message_box);
144 :
145 : int rc = crypto_box_afternm (message_box, message_plaintext,
146 57 : mlen, message_nonce, cn_precom);
147 57 : zmq_assert (rc == 0);
148 :
149 57 : rc = msg_->close ();
150 57 : zmq_assert (rc == 0);
151 :
152 57 : rc = msg_->init_size (16 + mlen - crypto_box_BOXZEROBYTES);
153 57 : zmq_assert (rc == 0);
154 :
155 57 : uint8_t *message = static_cast <uint8_t *> (msg_->data ());
156 :
157 : memcpy (message, "\x07MESSAGE", 8);
158 57 : memcpy (message + 8, message_nonce + 16, 8);
159 57 : memcpy (message + 16, message_box + crypto_box_BOXZEROBYTES,
160 57 : mlen - crypto_box_BOXZEROBYTES);
161 :
162 57 : free (message_plaintext);
163 57 : free (message_box);
164 :
165 57 : cn_nonce++;
166 :
167 57 : return 0;
168 : }
169 :
170 57 : int zmq::curve_client_t::decode (msg_t *msg_)
171 : {
172 57 : zmq_assert (state == connected);
173 :
174 57 : if (msg_->size () < 33) {
175 0 : errno = EPROTO;
176 0 : return -1;
177 : }
178 :
179 57 : const uint8_t *message = static_cast <uint8_t *> (msg_->data ());
180 57 : if (memcmp (message, "\x07MESSAGE", 8)) {
181 0 : errno = EPROTO;
182 0 : return -1;
183 : }
184 :
185 : uint8_t message_nonce [crypto_box_NONCEBYTES];
186 : memcpy (message_nonce, "CurveZMQMESSAGES", 16);
187 57 : memcpy (message_nonce + 16, message + 8, 8);
188 57 : uint64_t nonce = get_uint64(message + 8);
189 57 : if (nonce <= cn_peer_nonce) {
190 0 : errno = EPROTO;
191 0 : return -1;
192 : }
193 57 : cn_peer_nonce = nonce;
194 :
195 57 : const size_t clen = crypto_box_BOXZEROBYTES + (msg_->size () - 16);
196 :
197 57 : uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (clen));
198 57 : alloc_assert (message_plaintext);
199 :
200 57 : uint8_t *message_box = static_cast <uint8_t *> (malloc (clen));
201 57 : alloc_assert (message_box);
202 :
203 : memset (message_box, 0, crypto_box_BOXZEROBYTES);
204 57 : memcpy (message_box + crypto_box_BOXZEROBYTES,
205 57 : message + 16, msg_->size () - 16);
206 :
207 : int rc = crypto_box_open_afternm (message_plaintext, message_box,
208 57 : clen, message_nonce, cn_precom);
209 57 : if (rc == 0) {
210 57 : rc = msg_->close ();
211 57 : zmq_assert (rc == 0);
212 :
213 57 : rc = msg_->init_size (clen - 1 - crypto_box_ZEROBYTES);
214 57 : zmq_assert (rc == 0);
215 :
216 57 : const uint8_t flags = message_plaintext [crypto_box_ZEROBYTES];
217 57 : if (flags & 0x01)
218 3 : msg_->set_flags (msg_t::more);
219 57 : if (flags & 0x02)
220 51 : msg_->set_flags (msg_t::command);
221 :
222 : memcpy (msg_->data (),
223 : message_plaintext + crypto_box_ZEROBYTES + 1,
224 57 : msg_->size ());
225 : }
226 : else
227 0 : errno = EPROTO;
228 :
229 57 : free (message_plaintext);
230 57 : free (message_box);
231 :
232 57 : return rc;
233 : }
234 :
235 540 : zmq::mechanism_t::status_t zmq::curve_client_t::status () const
236 : {
237 540 : if (state == connected)
238 : return mechanism_t::ready;
239 : else
240 534 : if (state == error_received)
241 : return mechanism_t::error;
242 : else
243 534 : return mechanism_t::handshaking;
244 : }
245 :
246 45 : 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 45 : 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 45 : hello_nonce, server_key, cn_secret);
262 45 : if (rc == -1)
263 : return -1;
264 :
265 45 : rc = msg_->init_size (200);
266 45 : errno_assert (rc == 0);
267 45 : uint8_t *hello = static_cast <uint8_t *> (msg_->data ());
268 :
269 : memcpy (hello, "\x05HELLO", 6);
270 : // CurveZMQ major and minor version numbers
271 45 : memcpy (hello + 6, "\1\0", 2);
272 : // Anti-amplification padding
273 45 : memset (hello + 8, 0, 72);
274 : // Client public connection key
275 45 : memcpy (hello + 80, cn_public, crypto_box_PUBLICKEYBYTES);
276 : // Short nonce, prefixed by "CurveZMQHELLO---"
277 45 : memcpy (hello + 112, hello_nonce + 16, 8);
278 : // Signature, Box [64 * %x0](C'->S)
279 45 : memcpy (hello + 120, hello_box + crypto_box_BOXZEROBYTES, 80);
280 :
281 45 : cn_nonce++;
282 :
283 45 : return 0;
284 : }
285 :
286 33 : int zmq::curve_client_t::process_welcome (
287 : const uint8_t *msg_data, size_t msg_size)
288 : {
289 33 : if (msg_size != 168) {
290 0 : errno = EPROTO;
291 0 : 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 33 : memcpy (welcome_box + crypto_box_BOXZEROBYTES, msg_data + 24, 144);
301 :
302 : memcpy (welcome_nonce, "WELCOME-", 8);
303 33 : memcpy (welcome_nonce + 8, msg_data + 8, 16);
304 :
305 : int rc = crypto_box_open (welcome_plaintext, welcome_box,
306 : sizeof welcome_box,
307 33 : welcome_nonce, server_key, cn_secret);
308 33 : if (rc != 0) {
309 0 : errno = EPROTO;
310 0 : return -1;
311 : }
312 :
313 33 : memcpy (cn_server, welcome_plaintext + crypto_box_ZEROBYTES, 32);
314 33 : memcpy (cn_cookie, welcome_plaintext + crypto_box_ZEROBYTES + 32, 16 + 80);
315 :
316 : // Message independent precomputation
317 33 : rc = crypto_box_beforenm (cn_precom, cn_server, cn_secret);
318 33 : zmq_assert (rc == 0);
319 :
320 33 : state = send_initiate;
321 :
322 33 : return 0;
323 : }
324 :
325 33 : 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 33 : memcpy (vouch_plaintext + crypto_box_ZEROBYTES, cn_public, 32);
334 33 : memcpy (vouch_plaintext + crypto_box_ZEROBYTES + 32, server_key, 32);
335 :
336 : memcpy (vouch_nonce, "VOUCH---", 8);
337 33 : randombytes (vouch_nonce + 8, 16);
338 :
339 : int rc = crypto_box (vouch_box, vouch_plaintext,
340 : sizeof vouch_plaintext,
341 33 : vouch_nonce, cn_server, secret_key);
342 33 : 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 33 : 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 33 : uint8_t *ptr = initiate_plaintext + crypto_box_ZEROBYTES + 128;
361 :
362 : // Add socket type property
363 33 : const char *socket_type = socket_type_string (options.type);
364 33 : ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type));
365 :
366 : // Add identity property
367 33 : if (options.type == ZMQ_REQ
368 33 : || options.type == ZMQ_DEALER
369 33 : || options.type == ZMQ_ROUTER)
370 33 : ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
371 :
372 33 : const size_t mlen = ptr - initiate_plaintext;
373 :
374 : memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
375 33 : put_uint64 (initiate_nonce + 16, cn_nonce);
376 :
377 : rc = crypto_box (initiate_box, initiate_plaintext,
378 33 : mlen, initiate_nonce, cn_server, cn_secret);
379 33 : if (rc == -1)
380 : return -1;
381 :
382 33 : rc = msg_->init_size (113 + mlen - crypto_box_BOXZEROBYTES);
383 33 : errno_assert (rc == 0);
384 :
385 33 : 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 33 : memcpy (initiate + 9, cn_cookie, 96);
390 : // Short nonce, prefixed by "CurveZMQINITIATE"
391 33 : memcpy (initiate + 105, initiate_nonce + 16, 8);
392 : // Box [C + vouch + metadata](C'->S')
393 33 : memcpy (initiate + 113, initiate_box + crypto_box_BOXZEROBYTES,
394 33 : mlen - crypto_box_BOXZEROBYTES);
395 33 : cn_nonce++;
396 :
397 33 : return 0;
398 : }
399 :
400 6 : int zmq::curve_client_t::process_ready (
401 : const uint8_t *msg_data, size_t msg_size)
402 : {
403 6 : if (msg_size < 30) {
404 0 : errno = EPROTO;
405 0 : return -1;
406 : }
407 :
408 6 : 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 6 : msg_data + 14, clen - crypto_box_BOXZEROBYTES);
417 :
418 : memcpy (ready_nonce, "CurveZMQREADY---", 16);
419 6 : memcpy (ready_nonce + 16, msg_data + 6, 8);
420 6 : cn_peer_nonce = get_uint64(msg_data + 6);
421 :
422 : int rc = crypto_box_open_afternm (ready_plaintext, ready_box,
423 6 : clen, ready_nonce, cn_precom);
424 :
425 6 : if (rc != 0) {
426 0 : errno = EPROTO;
427 0 : return -1;
428 : }
429 :
430 : rc = parse_metadata (ready_plaintext + crypto_box_ZEROBYTES,
431 6 : clen - crypto_box_ZEROBYTES);
432 6 : if (rc == 0)
433 6 : state = connected;
434 :
435 6 : return rc;
436 : }
437 :
438 3 : int zmq::curve_client_t::process_error (
439 : const uint8_t *msg_data, size_t msg_size)
440 : {
441 3 : if (state != expect_welcome && state != expect_ready) {
442 0 : errno = EPROTO;
443 0 : return -1;
444 : }
445 3 : if (msg_size < 7) {
446 0 : errno = EPROTO;
447 0 : return -1;
448 : }
449 3 : const size_t error_reason_len = static_cast <size_t> (msg_data [6]);
450 3 : if (error_reason_len > msg_size - 7) {
451 3 : errno = EPROTO;
452 3 : return -1;
453 : }
454 0 : state = error_received;
455 0 : return 0;
456 : }
457 :
458 : #endif
|