curve_server.cpp 22.7 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
#ifdef ZMQ_HAVE_CURVE
34 35 36 37 38 39 40 41

#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_,
42
                                     const std::string &peer_address_,
43 44 45
                                     const options_t &options_) :
    mechanism_t (options_),
    session (session_),
46
    peer_address (peer_address_),
47
    state (expect_hello),
48
    cn_nonce (1),
49
    cn_peer_nonce(1)
50
{
51
    int rc;
52
    //  Fetch our secret key from socket options
53
    memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
54

55
    //  Generate short-term key pair
56
    rc = crypto_box_keypair (cn_public, cn_secret);
57 58 59 60 61 62 63
    zmq_assert (rc == 0);
}

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

64
int zmq::curve_server_t::next_handshake_command (msg_t *msg_)
65 66 67 68 69
{
    int rc = 0;

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

92
int zmq::curve_server_t::process_handshake_command (msg_t *msg_)
93 94 95 96 97 98 99 100 101 102 103
{
    int rc = 0;

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

119 120 121 122 123 124 125 126
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);
127
    put_uint64 (message_nonce + 16, cn_nonce);
128 129 130 131

    uint8_t flags = 0;
    if (msg_->flags () & msg_t::more)
        flags |= 0x01;
Jonathan Reams's avatar
Jonathan Reams committed
132 133
    if (msg_->flags () & msg_t::command)
        flags |= 0x02;
134 135 136 137 138 139 140 141 142 143 144 145 146

    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,
147
                                 mlen, message_nonce, cn_precom);
148 149 150 151 152 153 154 155 156 157
    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 ());

158
    memcpy (message, "\x07MESSAGE", 8);
159
    memcpy (message + 8, message_nonce + 16, 8);
160 161 162 163 164 165
    memcpy (message + 16, message_box + crypto_box_BOXZEROBYTES,
            mlen - crypto_box_BOXZEROBYTES);

    free (message_plaintext);
    free (message_box);

166
    cn_nonce++;
167 168 169 170 171 172 173 174 175

    return 0;
}

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

    if (msg_->size () < 33) {
176 177
        //  Temporary support for security debugging
        puts ("CURVE I: invalid CURVE client, sent malformed command");
178 179 180 181 182
        errno = EPROTO;
        return -1;
    }

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

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

    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,
213
                                      clen, message_nonce, cn_precom);
214 215 216 217 218 219 220 221 222 223
    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
224 225
        if (flags & 0x02)
            msg_->set_flags (msg_t::command);
226 227 228 229 230

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

    return rc;
}

242 243 244 245 246 247 248 249
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)
250 251 252
        state = status_code == "200"
            ? send_ready
            : send_error;
253 254 255
    return rc;
}

256
zmq::mechanism_t::status_t zmq::curve_server_t::status () const
257
{
258 259 260 261 262 263 264
    if (state == connected)
        return mechanism_t::ready;
    else
    if (state == error_sent)
        return mechanism_t::error;
    else
        return mechanism_t::handshaking;
265 266 267 268 269
}

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

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

284 285
    const uint8_t major = hello [6];
    const uint8_t minor = hello [7];
286 287

    if (major != 1 || minor != 0) {
288
        //  Temporary support for security debugging
289
        puts ("CURVE I: client HELLO has unknown version number");
290 291 292 293
        errno = EPROTO;
        return -1;
    }

294 295
    //  Save client's short-term public key (C')
    memcpy (cn_client, hello + 80, 32);
296 297 298 299 300 301 302

    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);
303
    cn_peer_nonce = get_uint64(hello + 112);
304 305 306 307 308 309 310

    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,
311
                              hello_nonce, cn_client, secret_key);
312
    if (rc != 0) {
313 314 315
        //  Temporary support for security debugging
        puts ("CURVE I: cannot open client HELLO -- wrong server key?");
        errno = EPROTO;
316
        return -1;
317
    }
318

319
    state = send_welcome;
320 321 322
    return rc;
}

323
int zmq::curve_server_t::produce_welcome (msg_t *msg_)
324 325 326 327 328 329 330 331 332 333 334 335 336
{
    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,
337
            cn_client, 32);
338
    memcpy (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32,
339
            cn_secret, 32);
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

    //  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);
361
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES, cn_public, 32);
362 363 364 365 366 367 368
    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,
369
                     welcome_nonce, cn_client, secret_key);
370 371
    if (rc == -1)
        return -1;
372 373 374 375 376

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

    uint8_t * const welcome = static_cast <uint8_t *> (msg_->data ());
377
    memcpy (welcome, "\x07WELCOME", 8);
378 379 380 381 382 383 384 385
    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_)
{
386
    if (msg_->size () < 257) {
387
        //  Temporary support for security debugging
388
        puts ("CURVE I: client INITIATE is not correct size");
389 390 391 392 393
        errno = EPROTO;
        return -1;
    }

    const uint8_t *initiate = static_cast <uint8_t *> (msg_->data ());
394
    if (memcmp (initiate, "\x08INITIATE", 9)) {
395
        //  Temporary support for security debugging
396
        puts ("CURVE I: client INITIATE has invalid command name");
397 398 399 400 401 402 403 404 405 406
        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);
407
    memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80);
408 409

    memcpy (cookie_nonce, "COOKIE--", 8);
410
    memcpy (cookie_nonce + 8, initiate + 9, 16);
411 412 413 414 415

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

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

431
    const size_t clen = (msg_->size () - 113) + crypto_box_BOXZEROBYTES;
432 433

    uint8_t initiate_nonce [crypto_box_NONCEBYTES];
434 435
    uint8_t initiate_plaintext [crypto_box_ZEROBYTES + 128 + 256];
    uint8_t initiate_box [crypto_box_BOXZEROBYTES + 144 + 256];
436 437 438 439

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

    memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
443
    memcpy (initiate_nonce + 16, initiate + 105, 8);
444
    cn_peer_nonce = get_uint64(initiate + 105);
445 446

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

    const uint8_t *client_key = initiate_plaintext + crypto_box_ZEROBYTES;

    uint8_t vouch_nonce [crypto_box_NONCEBYTES];
458 459
    uint8_t vouch_plaintext [crypto_box_ZEROBYTES + 64];
    uint8_t vouch_box [crypto_box_BOXZEROBYTES + 80];
460

461
    //  Open Box Box [C',S](C->S') and check contents
462 463
    memset (vouch_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (vouch_box + crypto_box_BOXZEROBYTES,
464
            initiate_plaintext + crypto_box_ZEROBYTES + 48, 80);
465 466 467 468 469 470 471

    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,
472
                          vouch_nonce, client_key, cn_secret);
473
    if (rc != 0) {
474
        //  Temporary support for security debugging
475
        puts ("CURVE I: cannot open client INITIATE vouch");
476 477 478 479
        errno = EPROTO;
        return -1;
    }

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

    //  Precompute connection secret from client key
489
    rc = crypto_box_beforenm (cn_precom, cn_client, cn_secret);
490 491
    zmq_assert (rc == 0);

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

510 511
    return parse_metadata (initiate_plaintext + crypto_box_ZEROBYTES + 128,
                           clen - crypto_box_ZEROBYTES - 128);
512 513
}

514
int zmq::curve_server_t::produce_ready (msg_t *msg_)
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
{
    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
532
        ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
533 534 535 536

    const size_t mlen = ptr - ready_plaintext;

    memcpy (ready_nonce, "CurveZMQREADY---", 16);
537
    put_uint64 (ready_nonce + 16, cn_nonce);
538 539

    int rc = crypto_box_afternm (ready_box, ready_plaintext,
540
                                 mlen, ready_nonce, cn_precom);
541 542
    zmq_assert (rc == 0);

543
    rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES);
544 545 546 547
    errno_assert (rc == 0);

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

548
    memcpy (ready, "\x05READY", 6);
549
    //  Short nonce, prefixed by "CurveZMQREADY---"
550
    memcpy (ready + 6, ready_nonce + 16, 8);
551
    //  Box [metadata](S'->C')
552
    memcpy (ready + 14, ready_box + crypto_box_BOXZEROBYTES,
553 554
            mlen - crypto_box_BOXZEROBYTES);

555
    cn_nonce++;
556 557 558 559

    return 0;
}

560 561 562 563 564 565 566 567 568 569 570 571
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;
}

572
int zmq::curve_server_t::send_zap_request (const uint8_t *key)
573 574 575 576 577 578 579 580 581
{
    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);
582
    if (rc != 0)
583
        return close_and_return (&msg, -1);
584 585 586 587 588 589 590

    //  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);
591
    if (rc != 0)
592
        return close_and_return (&msg, -1);
593

594
    //  Request ID frame
595 596 597 598 599
    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);
600
    if (rc != 0)
601
        return close_and_return (&msg, -1);
602 603

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

612 613 614 615 616 617
    //  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);
618
    if (rc != 0)
619
        return close_and_return (&msg, -1);
620

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

630 631 632 633 634 635
    //  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);
636
    if (rc != 0)
637
        return close_and_return (&msg, -1);
638 639 640 641 642 643

    //  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);
644
    if (rc != 0)
645
        return close_and_return (&msg, -1);
646 647

    return 0;
648 649 650 651 652
}

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

655 656
    //  Initialize all reply frames
    for (int i = 0; i < 7; i++) {
657 658 659 660
        rc = msg [i].init ();
        errno_assert (rc == 0);
    }

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

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

    //  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
        return close_and_return (msg, -1);
687 688
    }

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

    //  Status code frame
698
    if (msg [3].size () != 3) {
699
        //  Temporary support for security debugging
700
        puts ("CURVE I: ZAP handler rejected client authentication");
701
        errno = EACCES;
702
        return close_and_return (msg, -1);
703 704
    }

705 706 707
    //  Save status code
    status_code.assign (static_cast <char *> (msg [3].data ()), 3);

708 709 710
    //  Save user id
    set_user_id (msg [5].data (), msg [5].size ());

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

715
    if (rc != 0)
716
        return close_and_return (msg, -1);
717 718

    //  Close all reply frames
719
    for (int i = 0; i < 7; i++) {
720 721 722 723
        const int rc2 = msg [i].close ();
        errno_assert (rc2 == 0);
    }

724
    return 0;
725 726 727
}

#endif