curve_server.cpp 22.4 KB
Newer Older
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 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
25 26 27 28 29

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

30 31
#include "precompiled.hpp"
#include "macros.hpp"
32 33
#include "platform.hpp"

34
#ifdef ZMQ_HAVE_CURVE
35 36 37 38 39 40 41 42 43 44 45 46

#ifdef ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#endif

#include "msg.hpp"
#include "session_base.hpp"
#include "err.hpp"
#include "curve_server.hpp"
#include "wire.hpp"

zmq::curve_server_t::curve_server_t (session_base_t *session_,
47
                                     const std::string &peer_address_,
48 49 50
                                     const options_t &options_) :
    mechanism_t (options_),
    session (session_),
51
    peer_address (peer_address_),
52
    state (expect_hello),
53
    cn_nonce (1),
54
    cn_peer_nonce(1)
55
{
56
    int rc;
57
    //  Fetch our secret key from socket options
58
    memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
59

60
    //  Generate short-term key pair
61
    rc = crypto_box_keypair (cn_public, cn_secret);
62 63 64 65 66 67 68
    zmq_assert (rc == 0);
}

zmq::curve_server_t::~curve_server_t ()
{
}

69
int zmq::curve_server_t::next_handshake_command (msg_t *msg_)
70 71 72 73 74
{
    int rc = 0;

    switch (state) {
        case send_welcome:
75
            rc = produce_welcome (msg_);
76 77 78 79
            if (rc == 0)
                state = expect_initiate;
            break;
        case send_ready:
80
            rc = produce_ready (msg_);
81 82 83
            if (rc == 0)
                state = connected;
            break;
84 85 86 87 88
        case send_error:
            rc = produce_error (msg_);
            if (rc == 0)
                state = error_sent;
            break;
89 90 91 92 93 94 95 96
        default:
            errno = EAGAIN;
            rc = -1;
            break;
    }
    return rc;
}

97
int zmq::curve_server_t::process_handshake_command (msg_t *msg_)
98 99 100 101 102 103 104 105 106 107 108
{
    int rc = 0;

    switch (state) {
        case expect_hello:
            rc = process_hello (msg_);
            break;
        case expect_initiate:
            rc = process_initiate (msg_);
            break;
        default:
109 110
            //  Temporary support for security debugging
            puts ("CURVE I: invalid handshake command");
111
            errno = EPROTO;
112 113 114 115 116 117 118 119 120 121 122 123
            rc = -1;
            break;
    }
    if (rc == 0) {
        rc = msg_->close ();
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
    }
    return rc;
}

124 125 126 127 128 129 130 131
int zmq::curve_server_t::encode (msg_t *msg_)
{
    zmq_assert (state == connected);

    const size_t mlen = crypto_box_ZEROBYTES + 1 + msg_->size ();

    uint8_t message_nonce [crypto_box_NONCEBYTES];
    memcpy (message_nonce, "CurveZMQMESSAGES", 16);
132
    put_uint64 (message_nonce + 16, cn_nonce);
133 134 135 136

    uint8_t flags = 0;
    if (msg_->flags () & msg_t::more)
        flags |= 0x01;
Jonathan Reams's avatar
Jonathan Reams committed
137 138
    if (msg_->flags () & msg_t::command)
        flags |= 0x02;
139 140 141 142 143 144 145 146 147 148 149 150 151

    uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (mlen));
    alloc_assert (message_plaintext);

    memset (message_plaintext, 0, crypto_box_ZEROBYTES);
    message_plaintext [crypto_box_ZEROBYTES] = flags;
    memcpy (message_plaintext + crypto_box_ZEROBYTES + 1,
            msg_->data (), msg_->size ());

    uint8_t *message_box = static_cast <uint8_t *> (malloc (mlen));
    alloc_assert (message_box);

    int rc = crypto_box_afternm (message_box, message_plaintext,
152
                                 mlen, message_nonce, cn_precom);
153 154 155 156 157 158 159 160 161 162
    zmq_assert (rc == 0);

    rc = msg_->close ();
    zmq_assert (rc == 0);

    rc = msg_->init_size (16 + mlen - crypto_box_BOXZEROBYTES);
    zmq_assert (rc == 0);

    uint8_t *message = static_cast <uint8_t *> (msg_->data ());

163
    memcpy (message, "\x07MESSAGE", 8);
164
    memcpy (message + 8, message_nonce + 16, 8);
165 166 167 168 169 170
    memcpy (message + 16, message_box + crypto_box_BOXZEROBYTES,
            mlen - crypto_box_BOXZEROBYTES);

    free (message_plaintext);
    free (message_box);

171
    cn_nonce++;
172 173 174 175 176 177 178 179 180

    return 0;
}

int zmq::curve_server_t::decode (msg_t *msg_)
{
    zmq_assert (state == connected);

    if (msg_->size () < 33) {
181 182
        //  Temporary support for security debugging
        puts ("CURVE I: invalid CURVE client, sent malformed command");
183 184 185 186 187
        errno = EPROTO;
        return -1;
    }

    const uint8_t *message = static_cast <uint8_t *> (msg_->data ());
188
    if (memcmp (message, "\x07MESSAGE", 8)) {
189 190
        //  Temporary support for security debugging
        puts ("CURVE I: invalid CURVE client, did not send MESSAGE");
191 192 193 194 195 196 197
        errno = EPROTO;
        return -1;
    }

    uint8_t message_nonce [crypto_box_NONCEBYTES];
    memcpy (message_nonce, "CurveZMQMESSAGEC", 16);
    memcpy (message_nonce + 16, message + 8, 8);
198 199 200 201 202 203
    uint64_t nonce = get_uint64(message + 8);
    if (nonce <= cn_peer_nonce) {
        errno = EPROTO;
        return -1;
    }
    cn_peer_nonce = nonce;
204 205 206 207 208 209 210 211 212 213 214 215 216 217

    const size_t clen = crypto_box_BOXZEROBYTES + msg_->size () - 16;

    uint8_t *message_plaintext = static_cast <uint8_t *> (malloc (clen));
    alloc_assert (message_plaintext);

    uint8_t *message_box = static_cast <uint8_t *> (malloc (clen));
    alloc_assert (message_box);

    memset (message_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (message_box + crypto_box_BOXZEROBYTES,
            message + 16, msg_->size () - 16);

    int rc = crypto_box_open_afternm (message_plaintext, message_box,
218
                                      clen, message_nonce, cn_precom);
219 220 221 222 223 224 225 226 227 228
    if (rc == 0) {
        rc = msg_->close ();
        zmq_assert (rc == 0);

        rc = msg_->init_size (clen - 1 - crypto_box_ZEROBYTES);
        zmq_assert (rc == 0);

        const uint8_t flags = message_plaintext [crypto_box_ZEROBYTES];
        if (flags & 0x01)
            msg_->set_flags (msg_t::more);
Jonathan Reams's avatar
Jonathan Reams committed
229 230
        if (flags & 0x02)
            msg_->set_flags (msg_t::command);
231 232 233 234 235

        memcpy (msg_->data (),
                message_plaintext + crypto_box_ZEROBYTES + 1,
                msg_->size ());
    }
236 237 238
    else {
        //  Temporary support for security debugging
        puts ("CURVE I: connection key used for MESSAGE is wrong");
239
        errno = EPROTO;
240
    }
241 242 243 244 245 246
    free (message_plaintext);
    free (message_box);

    return rc;
}

247 248 249 250 251 252 253 254
int zmq::curve_server_t::zap_msg_available ()
{
    if (state != expect_zap_reply) {
        errno = EFSM;
        return -1;
    }
    const int rc = receive_and_process_zap_reply ();
    if (rc == 0)
255 256 257
        state = status_code == "200"
            ? send_ready
            : send_error;
258 259 260
    return rc;
}

261
zmq::mechanism_t::status_t zmq::curve_server_t::status () const
262
{
263 264 265 266 267 268 269
    if (state == connected)
        return mechanism_t::ready;
    else
    if (state == error_sent)
        return mechanism_t::error;
    else
        return mechanism_t::handshaking;
270 271 272 273 274
}

int zmq::curve_server_t::process_hello (msg_t *msg_)
{
    if (msg_->size () != 200) {
275
        //  Temporary support for security debugging
276
        puts ("CURVE I: client HELLO is not correct size");
277 278 279 280 281
        errno = EPROTO;
        return -1;
    }

    const uint8_t * const hello = static_cast <uint8_t *> (msg_->data ());
282
    if (memcmp (hello, "\x05HELLO", 6)) {
283
        //  Temporary support for security debugging
284
        puts ("CURVE I: client HELLO has invalid command name");
285 286 287 288
        errno = EPROTO;
        return -1;
    }

289 290
    const uint8_t major = hello [6];
    const uint8_t minor = hello [7];
291 292

    if (major != 1 || minor != 0) {
293
        //  Temporary support for security debugging
294
        puts ("CURVE I: client HELLO has unknown version number");
295 296 297 298
        errno = EPROTO;
        return -1;
    }

299 300
    //  Save client's short-term public key (C')
    memcpy (cn_client, hello + 80, 32);
301 302 303 304 305 306 307

    uint8_t hello_nonce [crypto_box_NONCEBYTES];
    uint8_t hello_plaintext [crypto_box_ZEROBYTES + 64];
    uint8_t hello_box [crypto_box_BOXZEROBYTES + 80];

    memcpy (hello_nonce, "CurveZMQHELLO---", 16);
    memcpy (hello_nonce + 16, hello + 112, 8);
308
    cn_peer_nonce = get_uint64(hello + 112);
309 310 311 312 313 314 315

    memset (hello_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80);

    //  Open Box [64 * %x0](C'->S)
    int rc = crypto_box_open (hello_plaintext, hello_box,
                              sizeof hello_box,
316
                              hello_nonce, cn_client, secret_key);
317
    if (rc != 0) {
318
        //  Temporary support for security debugging
319
        puts ("CURVE I: cannot open client HELLO -- wrong server key?");
320 321 322 323
        errno = EPROTO;
        return -1;
    }

324
    state = send_welcome;
325 326 327
    return rc;
}

328
int zmq::curve_server_t::produce_welcome (msg_t *msg_)
329 330 331 332 333 334 335 336 337 338 339 340 341
{
    uint8_t cookie_nonce [crypto_secretbox_NONCEBYTES];
    uint8_t cookie_plaintext [crypto_secretbox_ZEROBYTES + 64];
    uint8_t cookie_ciphertext [crypto_secretbox_BOXZEROBYTES + 80];

    //  Create full nonce for encryption
    //  8-byte prefix plus 16-byte random nonce
    memcpy (cookie_nonce, "COOKIE--", 8);
    randombytes (cookie_nonce + 8, 16);

    //  Generate cookie = Box [C' + s'](t)
    memset (cookie_plaintext, 0, crypto_secretbox_ZEROBYTES);
    memcpy (cookie_plaintext + crypto_secretbox_ZEROBYTES,
342
            cn_client, 32);
343
    memcpy (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32,
344
            cn_secret, 32);
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

    //  Generate fresh cookie key
    randombytes (cookie_key, crypto_secretbox_KEYBYTES);

    //  Encrypt using symmetric cookie key
    int rc = crypto_secretbox (cookie_ciphertext, cookie_plaintext,
                               sizeof cookie_plaintext,
                               cookie_nonce, cookie_key);
    zmq_assert (rc == 0);

    uint8_t welcome_nonce [crypto_box_NONCEBYTES];
    uint8_t welcome_plaintext [crypto_box_ZEROBYTES + 128];
    uint8_t welcome_ciphertext [crypto_box_BOXZEROBYTES + 144];

    //  Create full nonce for encryption
    //  8-byte prefix plus 16-byte random nonce
    memcpy (welcome_nonce, "WELCOME-", 8);
    randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8);

    //  Create 144-byte Box [S' + cookie](S->C')
    memset (welcome_plaintext, 0, crypto_box_ZEROBYTES);
366
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES, cn_public, 32);
367 368 369 370 371 372 373
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES + 32,
            cookie_nonce + 8, 16);
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES + 48,
            cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80);

    rc = crypto_box (welcome_ciphertext, welcome_plaintext,
                     sizeof welcome_plaintext,
374
                     welcome_nonce, cn_client, secret_key);
375 376
    if (rc == -1)
        return -1;
377 378 379 380 381

    rc = msg_->init_size (168);
    errno_assert (rc == 0);

    uint8_t * const welcome = static_cast <uint8_t *> (msg_->data ());
382
    memcpy (welcome, "\x07WELCOME", 8);
383 384 385 386 387 388 389 390
    memcpy (welcome + 8, welcome_nonce + 8, 16);
    memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144);

    return 0;
}

int zmq::curve_server_t::process_initiate (msg_t *msg_)
{
391
    if (msg_->size () < 257) {
392
        //  Temporary support for security debugging
393
        puts ("CURVE I: client INITIATE is not correct size");
394 395 396 397 398
        errno = EPROTO;
        return -1;
    }

    const uint8_t *initiate = static_cast <uint8_t *> (msg_->data ());
399
    if (memcmp (initiate, "\x08INITIATE", 9)) {
400
        //  Temporary support for security debugging
401
        puts ("CURVE I: client INITIATE has invalid command name");
402 403 404 405 406 407 408 409 410 411
        errno = EPROTO;
        return -1;
    }

    uint8_t cookie_nonce [crypto_secretbox_NONCEBYTES];
    uint8_t cookie_plaintext [crypto_secretbox_ZEROBYTES + 64];
    uint8_t cookie_box [crypto_secretbox_BOXZEROBYTES + 80];

    //  Open Box [C' + s'](t)
    memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES);
412
    memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80);
413 414

    memcpy (cookie_nonce, "COOKIE--", 8);
415
    memcpy (cookie_nonce + 8, initiate + 9, 16);
416 417 418 419 420

    int rc = crypto_secretbox_open (cookie_plaintext, cookie_box,
                                    sizeof cookie_box,
                                    cookie_nonce, cookie_key);
    if (rc != 0) {
421
        //  Temporary support for security debugging
422
        puts ("CURVE I: cannot open client INITIATE cookie");
423 424 425 426 427
        errno = EPROTO;
        return -1;
    }

    //  Check cookie plain text is as expected [C' + s']
428 429
    if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, cn_client, 32)
    ||  memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32, cn_secret, 32)) {
430
        //  Temporary support for security debugging
431
        puts ("CURVE I: client INITIATE cookie is not valid");
432
        errno = EPROTO;
433 434 435
        return -1;
    }

436
    const size_t clen = (msg_->size () - 113) + crypto_box_BOXZEROBYTES;
437 438

    uint8_t initiate_nonce [crypto_box_NONCEBYTES];
439 440
    uint8_t initiate_plaintext [crypto_box_ZEROBYTES + 128 + 256];
    uint8_t initiate_box [crypto_box_BOXZEROBYTES + 144 + 256];
441 442 443 444

    //  Open Box [C + vouch + metadata](C'->S')
    memset (initiate_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (initiate_box + crypto_box_BOXZEROBYTES,
445
            initiate + 113, clen - crypto_box_BOXZEROBYTES);
446 447

    memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
448
    memcpy (initiate_nonce + 16, initiate + 105, 8);
449
    cn_peer_nonce = get_uint64(initiate + 105);
450 451

    rc = crypto_box_open (initiate_plaintext, initiate_box,
452
                          clen, initiate_nonce, cn_client, cn_secret);
453
    if (rc != 0) {
454
        //  Temporary support for security debugging
455
        puts ("CURVE I: cannot open client INITIATE");
456 457 458 459 460 461 462
        errno = EPROTO;
        return -1;
    }

    const uint8_t *client_key = initiate_plaintext + crypto_box_ZEROBYTES;

    uint8_t vouch_nonce [crypto_box_NONCEBYTES];
463 464
    uint8_t vouch_plaintext [crypto_box_ZEROBYTES + 64];
    uint8_t vouch_box [crypto_box_BOXZEROBYTES + 80];
465

466
    //  Open Box Box [C',S](C->S') and check contents
467 468
    memset (vouch_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (vouch_box + crypto_box_BOXZEROBYTES,
469
            initiate_plaintext + crypto_box_ZEROBYTES + 48, 80);
470 471 472 473 474 475 476

    memcpy (vouch_nonce, "VOUCH---", 8);
    memcpy (vouch_nonce + 8,
            initiate_plaintext + crypto_box_ZEROBYTES + 32, 16);

    rc = crypto_box_open (vouch_plaintext, vouch_box,
                          sizeof vouch_box,
477
                          vouch_nonce, client_key, cn_secret);
478
    if (rc != 0) {
479
        //  Temporary support for security debugging
480
        puts ("CURVE I: cannot open client INITIATE vouch");
481 482 483 484
        errno = EPROTO;
        return -1;
    }

485 486
    //  What we decrypted must be the client's short-term public key
    if (memcmp (vouch_plaintext + crypto_box_ZEROBYTES, cn_client, 32)) {
487 488
        //  Temporary support for security debugging
        puts ("CURVE I: invalid handshake from client (public key)");
489 490 491 492 493
        errno = EPROTO;
        return -1;
    }

    //  Precompute connection secret from client key
494
    rc = crypto_box_beforenm (cn_precom, cn_client, cn_secret);
495 496
    zmq_assert (rc == 0);

497 498 499 500 501
    //  Use ZAP protocol (RFC 27) to authenticate the user.
    rc = session->zap_connect ();
    if (rc == 0) {
        send_zap_request (client_key);
        rc = receive_and_process_zap_reply ();
502 503 504 505 506 507 508 509 510
        if (rc == 0)
            state = status_code == "200"
                ? send_ready
                : send_error;
        else
        if (errno == EAGAIN)
            state = expect_zap_reply;
        else
            return -1;
511
    }
512 513
    else
        state = send_ready;
514

515 516
    return parse_metadata (initiate_plaintext + crypto_box_ZEROBYTES + 128,
                           clen - crypto_box_ZEROBYTES - 128);
517 518
}

519
int zmq::curve_server_t::produce_ready (msg_t *msg_)
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
{
    uint8_t ready_nonce [crypto_box_NONCEBYTES];
    uint8_t ready_plaintext [crypto_box_ZEROBYTES + 256];
    uint8_t ready_box [crypto_box_BOXZEROBYTES + 16 + 256];

    //  Create Box [metadata](S'->C')
    memset (ready_plaintext, 0, crypto_box_ZEROBYTES);
    uint8_t *ptr = ready_plaintext + crypto_box_ZEROBYTES;

    //  Add socket type property
    const char *socket_type = socket_type_string (options.type);
    ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type));

    //  Add identity property
    if (options.type == ZMQ_REQ
    ||  options.type == ZMQ_DEALER
    ||  options.type == ZMQ_ROUTER)
Pieter Hintjens's avatar
Pieter Hintjens committed
537
        ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
538 539 540 541

    const size_t mlen = ptr - ready_plaintext;

    memcpy (ready_nonce, "CurveZMQREADY---", 16);
542
    put_uint64 (ready_nonce + 16, cn_nonce);
543 544

    int rc = crypto_box_afternm (ready_box, ready_plaintext,
545
                                 mlen, ready_nonce, cn_precom);
546 547
    zmq_assert (rc == 0);

548
    rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES);
549 550 551 552
    errno_assert (rc == 0);

    uint8_t *ready = static_cast <uint8_t *> (msg_->data ());

553
    memcpy (ready, "\x05READY", 6);
554
    //  Short nonce, prefixed by "CurveZMQREADY---"
555
    memcpy (ready + 6, ready_nonce + 16, 8);
556
    //  Box [metadata](S'->C')
557
    memcpy (ready + 14, ready_box + crypto_box_BOXZEROBYTES,
558 559
            mlen - crypto_box_BOXZEROBYTES);

560
    cn_nonce++;
561 562 563 564

    return 0;
}

565 566 567 568 569 570 571 572 573 574 575 576
int zmq::curve_server_t::produce_error (msg_t *msg_) const
{
    zmq_assert (status_code.length () == 3);
    const int rc = msg_->init_size (6 + 1 + status_code.length ());
    zmq_assert (rc == 0);
    char *msg_data = static_cast <char *> (msg_->data ());
    memcpy (msg_data, "\5ERROR", 6);
    msg_data [6] = sizeof status_code;
    memcpy (msg_data + 7, status_code.c_str (), status_code.length ());
    return 0;
}

577
void zmq::curve_server_t::send_zap_request (const uint8_t *key)
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
{
    int rc;
    msg_t msg;

    //  Address delimiter frame
    rc = msg.init ();
    errno_assert (rc == 0);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Version frame
    rc = msg.init_size (3);
    errno_assert (rc == 0);
    memcpy (msg.data (), "1.0", 3);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

597
    //  Request ID frame
598 599 600 601 602 603 604 605
    rc = msg.init_size (1);
    errno_assert (rc == 0);
    memcpy (msg.data (), "1", 1);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Domain frame
606
    rc = msg.init_size (options.zap_domain.length ());
607
    errno_assert (rc == 0);
608
    memcpy (msg.data (), options.zap_domain.c_str (), options.zap_domain.length ());
609 610 611 612
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

613 614 615 616 617 618 619 620
    //  Address frame
    rc = msg.init_size (peer_address.length ());
    errno_assert (rc == 0);
    memcpy (msg.data (), peer_address.c_str (), peer_address.length ());
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

621
    //  Identity frame
622
    rc = msg.init_size (options.identity_size);
623
    errno_assert (rc == 0);
624 625 626 627 628
    memcpy (msg.data (), options.identity, options.identity_size);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
    //  Mechanism frame
    rc = msg.init_size (5);
    errno_assert (rc == 0);
    memcpy (msg.data (), "CURVE", 5);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Credentials frame
    rc = msg.init_size (crypto_box_PUBLICKEYBYTES);
    errno_assert (rc == 0);
    memcpy (msg.data (), key, crypto_box_PUBLICKEYBYTES);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);
}

int zmq::curve_server_t::receive_and_process_zap_reply ()
{
    int rc = 0;
648
    msg_t msg [7];  //  ZAP reply consists of 7 frames
649

650 651
    //  Initialize all reply frames
    for (int i = 0; i < 7; i++) {
652 653 654 655
        rc = msg [i].init ();
        errno_assert (rc == 0);
    }

656
    for (int i = 0; i < 7; i++) {
657 658 659
        rc = session->read_zap_msg (&msg [i]);
        if (rc == -1)
            break;
660
        if ((msg [i].flags () & msg_t::more) == (i < 6? 0: msg_t::more)) {
661 662
            //  Temporary support for security debugging
            puts ("CURVE I: ZAP handler sent incomplete reply message");
663 664 665 666 667 668 669 670 671 672 673
            errno = EPROTO;
            rc = -1;
            break;
        }
    }

    if (rc != 0)
        goto error;

    //  Address delimiter frame
    if (msg [0].size () > 0) {
674 675
        //  Temporary support for security debugging
        puts ("CURVE I: ZAP handler sent malformed reply message");
676
        errno = EPROTO;
677
        rc = -1;
678 679 680 681 682
        goto error;
    }

    //  Version frame
    if (msg [1].size () != 3 || memcmp (msg [1].data (), "1.0", 3)) {
683 684
        //  Temporary support for security debugging
        puts ("CURVE I: ZAP handler sent bad version number");
685
        errno = EPROTO;
686
        rc = -1;
687 688 689
        goto error;
    }

690
    //  Request id frame
691
    if (msg [2].size () != 1 || memcmp (msg [2].data (), "1", 1)) {
692 693
        //  Temporary support for security debugging
        puts ("CURVE I: ZAP handler sent bad request ID");
694
        errno = EPROTO;
695
        rc = -1;
696 697 698 699
        goto error;
    }

    //  Status code frame
700
    if (msg [3].size () != 3) {
701
        //  Temporary support for security debugging
702
        puts ("CURVE I: ZAP handler rejected client authentication");
703
        errno = EACCES;
704
        rc = -1;
705 706 707
        goto error;
    }

708 709 710
    //  Save status code
    status_code.assign (static_cast <char *> (msg [3].data ()), 3);

711 712 713
    //  Save user id
    set_user_id (msg [5].data (), msg [5].size ());

714 715
    //  Process metadata frame
    rc = parse_metadata (static_cast <const unsigned char*> (msg [6].data ()),
716
                         msg [6].size (), true);
717

718
error:
719
    for (int i = 0; i < 7; i++) {
720 721 722 723 724 725 726 727
        const int rc2 = msg [i].close ();
        errno_assert (rc2 == 0);
    }

    return rc;
}

#endif