curve_client.cpp 13.9 KB
Newer Older
1
/*
2
    Copyright (c) 2007-2015 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

    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/>.
*/

#include "platform.hpp"

#ifdef HAVE_LIBSODIUM

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

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

zmq::curve_client_t::curve_client_t (const options_t &options_) :
    mechanism_t (options_),
46
    state (send_hello),
47 48
    cn_nonce(1),
    cn_peer_nonce(1),
49
    sync()
50
{
51
    int rc;
52 53 54
    memcpy (public_key, options_.curve_public_key, crypto_box_PUBLICKEYBYTES);
    memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
    memcpy (server_key, options_.curve_server_key, crypto_box_PUBLICKEYBYTES);
55 56 57 58 59 60
    scoped_lock_t lock (sync);
#if defined(HAVE_TWEETNACL)
    // allow opening of /dev/urandom
    unsigned char tmpbytes[4];
    randombytes(tmpbytes, 4);
#else
61 62
    rc = sodium_init ();
    zmq_assert (rc != -1);
63
#endif
64

65
    //  Generate short-term key pair
66
    rc = crypto_box_keypair (cn_public, cn_secret);
67 68 69 70 71 72 73
    zmq_assert (rc == 0);
}

zmq::curve_client_t::~curve_client_t ()
{
}

74
int zmq::curve_client_t::next_handshake_command (msg_t *msg_)
75 76 77 78 79
{
    int rc = 0;

    switch (state) {
        case send_hello:
80
            rc = produce_hello (msg_);
81 82 83 84
            if (rc == 0)
                state = expect_welcome;
            break;
        case send_initiate:
85
            rc = produce_initiate (msg_);
86 87 88 89 90 91 92 93 94 95
            if (rc == 0)
                state = expect_ready;
            break;
        default:
            errno = EAGAIN;
            rc = -1;
    }
    return rc;
}

96
int zmq::curve_client_t::process_handshake_command (msg_t *msg_)
97
{
98 99 100
    const unsigned char *msg_data =
        static_cast <unsigned char *> (msg_->data ());
    const size_t msg_size = msg_->size ();
101

102 103 104 105 106 107 108 109 110 111 112 113
    int rc = 0;
    if (msg_size >= 8 && !memcmp (msg_data, "\7WELCOME", 8))
        rc = process_welcome (msg_data, msg_size);
    else
    if (msg_size >= 6 && !memcmp (msg_data, "\5READY", 6))
        rc = process_ready (msg_data, msg_size);
    else
    if (msg_size >= 6 && !memcmp (msg_data, "\5ERROR", 6))
        rc = process_error (msg_data, msg_size);
    else {
        errno = EPROTO;
        rc = -1;
114
    }
115

116 117 118 119 120 121
    if (rc == 0) {
        rc = msg_->close ();
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
    }
122

123 124 125
    return rc;
}

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

    uint8_t flags = 0;
    if (msg_->flags () & msg_t::more)
        flags |= 0x01;
Jonathan Reams's avatar
Jonathan Reams committed
133 134
    if (msg_->flags () & msg_t::command)
        flags |= 0x02;
135 136 137

    uint8_t message_nonce [crypto_box_NONCEBYTES];
    memcpy (message_nonce, "CurveZMQMESSAGEC", 16);
138
    put_uint64 (message_nonce + 16, cn_nonce);
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

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

    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,
154
                                 mlen, message_nonce, cn_precom);
155 156 157 158 159 160 161 162 163 164
    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 ());

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

    free (message_plaintext);
    free (message_box);

173
    cn_nonce++;
174 175 176 177 178 179 180 181 182 183 184 185 186 187

    return 0;
}

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

    if (msg_->size () < 33) {
        errno = EPROTO;
        return -1;
    }

    const uint8_t *message = static_cast <uint8_t *> (msg_->data ());
188
    if (memcmp (message, "\x07MESSAGE", 8)) {
189 190 191 192 193 194 195
        errno = EPROTO;
        return -1;
    }

    uint8_t message_nonce [crypto_box_NONCEBYTES];
    memcpy (message_nonce, "CurveZMQMESSAGES", 16);
    memcpy (message_nonce + 16, message + 8, 8);
196 197 198 199 200 201 202
    uint64_t nonce = get_uint64(message + 8);
    if (nonce <= cn_peer_nonce) {
        errno = EPROTO;
        return -1;
    }
    cn_peer_nonce = nonce;

203 204 205 206 207 208 209 210 211 212 213 214 215 216

    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,
217
                                      clen, message_nonce, cn_precom);
218 219 220 221 222 223 224 225 226 227
    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
228 229
        if (flags & 0x02)
            msg_->set_flags (msg_t::command);
230 231 232 233 234 235 236 237 238 239 240 241 242 243

        memcpy (msg_->data (),
                message_plaintext + crypto_box_ZEROBYTES + 1,
                msg_->size ());
    }
    else
        errno = EPROTO;

    free (message_plaintext);
    free (message_box);

    return rc;
}

244
zmq::mechanism_t::status_t zmq::curve_client_t::status () const
245
{
246 247 248 249 250 251 252
    if (state == connected)
        return mechanism_t::ready;
    else
    if (state == error_received)
        return mechanism_t::error;
    else
        return mechanism_t::handshaking;
253 254
}

255
int zmq::curve_client_t::produce_hello (msg_t *msg_)
256 257 258 259 260 261 262
{
    uint8_t hello_nonce [crypto_box_NONCEBYTES];
    uint8_t hello_plaintext [crypto_box_ZEROBYTES + 64];
    uint8_t hello_box [crypto_box_BOXZEROBYTES + 80];

    //  Prepare the full nonce
    memcpy (hello_nonce, "CurveZMQHELLO---", 16);
263
    put_uint64 (hello_nonce + 16, cn_nonce);
264 265 266 267 268 269

    //  Create Box [64 * %x0](C'->S)
    memset (hello_plaintext, 0, sizeof hello_plaintext);

    int rc = crypto_box (hello_box, hello_plaintext,
                         sizeof hello_plaintext,
270
                         hello_nonce, server_key, cn_secret);
271 272 273 274 275 276
    zmq_assert (rc == 0);

    rc = msg_->init_size (200);
    errno_assert (rc == 0);
    uint8_t *hello = static_cast <uint8_t *> (msg_->data ());

277
    memcpy (hello, "\x05HELLO", 6);
278
    //  CurveZMQ major and minor version numbers
279
    memcpy (hello + 6, "\1\0", 2);
280
    //  Anti-amplification padding
281
    memset (hello + 8, 0, 72);
282
    //  Client public connection key
283
    memcpy (hello + 80, cn_public, crypto_box_PUBLICKEYBYTES);
284 285 286 287 288
    //  Short nonce, prefixed by "CurveZMQHELLO---"
    memcpy (hello + 112, hello_nonce + 16, 8);
    //  Signature, Box [64 * %x0](C'->S)
    memcpy (hello + 120, hello_box + crypto_box_BOXZEROBYTES, 80);

289
    cn_nonce++;
290 291 292 293

    return 0;
}

294 295
int zmq::curve_client_t::process_welcome (
        const uint8_t *msg_data, size_t msg_size)
296
{
297
    if (msg_size != 168) {
298 299 300 301 302 303 304 305 306 307
        errno = EPROTO;
        return -1;
    }

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

    //  Open Box [S' + cookie](C'->S)
    memset (welcome_box, 0, crypto_box_BOXZEROBYTES);
308
    memcpy (welcome_box + crypto_box_BOXZEROBYTES, msg_data + 24, 144);
309 310

    memcpy (welcome_nonce, "WELCOME-", 8);
311
    memcpy (welcome_nonce + 8, msg_data + 8, 16);
312 313 314

    int rc = crypto_box_open (welcome_plaintext, welcome_box,
                              sizeof welcome_box,
315
                              welcome_nonce, server_key, cn_secret);
316 317 318 319 320
    if (rc != 0) {
        errno = EPROTO;
        return -1;
    }

321 322
    memcpy (cn_server, welcome_plaintext + crypto_box_ZEROBYTES, 32);
    memcpy (cn_cookie, welcome_plaintext + crypto_box_ZEROBYTES + 32, 16 + 80);
323 324

    //  Message independent precomputation
325
    rc = crypto_box_beforenm (cn_precom, cn_server, cn_secret);
326 327
    zmq_assert (rc == 0);

328 329
    state = send_initiate;

330 331 332
    return 0;
}

333
int zmq::curve_client_t::produce_initiate (msg_t *msg_)
334 335
{
    uint8_t vouch_nonce [crypto_box_NONCEBYTES];
336 337
    uint8_t vouch_plaintext [crypto_box_ZEROBYTES + 64];
    uint8_t vouch_box [crypto_box_BOXZEROBYTES + 80];
338

339
    //  Create vouch = Box [C',S](C->S')
340
    memset (vouch_plaintext, 0, crypto_box_ZEROBYTES);
341
    memcpy (vouch_plaintext + crypto_box_ZEROBYTES, cn_public, 32);
342
    memcpy (vouch_plaintext + crypto_box_ZEROBYTES + 32, server_key, 32);
343 344 345 346 347 348

    memcpy (vouch_nonce, "VOUCH---", 8);
    randombytes (vouch_nonce + 8, 16);

    int rc = crypto_box (vouch_box, vouch_plaintext,
                         sizeof vouch_plaintext,
349
                         vouch_nonce, cn_server, secret_key);
350 351
    zmq_assert (rc == 0);

352
    //  Assume here that metadata is limited to 256 bytes
353
    uint8_t initiate_nonce [crypto_box_NONCEBYTES];
354 355
    uint8_t initiate_plaintext [crypto_box_ZEROBYTES + 128 + 256];
    uint8_t initiate_box [crypto_box_BOXZEROBYTES + 144 + 256];
356 357 358

    //  Create Box [C + vouch + metadata](C'->S')
    memset (initiate_plaintext, 0, crypto_box_ZEROBYTES);
359
    memcpy (initiate_plaintext + crypto_box_ZEROBYTES,
360
            public_key, 32);
361 362 363
    memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 32,
            vouch_nonce + 8, 16);
    memcpy (initiate_plaintext + crypto_box_ZEROBYTES + 48,
364
            vouch_box + crypto_box_BOXZEROBYTES, 80);
365

366 367
    //  Metadata starts after vouch
    uint8_t *ptr = initiate_plaintext + crypto_box_ZEROBYTES + 128;
368 369 370 371 372 373 374 375 376

    //  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
377
        ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
378 379 380 381

    const size_t mlen = ptr - initiate_plaintext;

    memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
382
    put_uint64 (initiate_nonce + 16, cn_nonce);
383 384

    rc = crypto_box (initiate_box, initiate_plaintext,
385
                     mlen, initiate_nonce, cn_server, cn_secret);
386 387
    zmq_assert (rc == 0);

388
    rc = msg_->init_size (113 + mlen - crypto_box_BOXZEROBYTES);
389 390 391 392
    errno_assert (rc == 0);

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

393
    memcpy (initiate, "\x08INITIATE", 9);
394
    //  Cookie provided by the server in the WELCOME command
395
    memcpy (initiate + 9, cn_cookie, 96);
396
    //  Short nonce, prefixed by "CurveZMQINITIATE"
397
    memcpy (initiate + 105, initiate_nonce + 16, 8);
398
    //  Box [C + vouch + metadata](C'->S')
399
    memcpy (initiate + 113, initiate_box + crypto_box_BOXZEROBYTES,
400
            mlen - crypto_box_BOXZEROBYTES);
401
    cn_nonce++;
402 403 404 405

    return 0;
}

406 407
int zmq::curve_client_t::process_ready (
        const uint8_t *msg_data, size_t msg_size)
408
{
409
    if (msg_size < 30) {
410 411 412 413
        errno = EPROTO;
        return -1;
    }

414
    const size_t clen = (msg_size - 14) + crypto_box_BOXZEROBYTES;
415 416 417 418 419 420 421

    uint8_t ready_nonce [crypto_box_NONCEBYTES];
    uint8_t ready_plaintext [crypto_box_ZEROBYTES + 256];
    uint8_t ready_box [crypto_box_BOXZEROBYTES + 16 + 256];

    memset (ready_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (ready_box + crypto_box_BOXZEROBYTES,
422
            msg_data + 14, clen - crypto_box_BOXZEROBYTES);
423 424

    memcpy (ready_nonce, "CurveZMQREADY---", 16);
425
    memcpy (ready_nonce + 16, msg_data + 6, 8);
426
    cn_peer_nonce = get_uint64(msg_data + 6);
427 428

    int rc = crypto_box_open_afternm (ready_plaintext, ready_box,
429
                                      clen, ready_nonce, cn_precom);
430 431 432 433 434 435

    if (rc != 0) {
        errno = EPROTO;
        return -1;
    }

436 437
    rc = parse_metadata (ready_plaintext + crypto_box_ZEROBYTES,
                         clen - crypto_box_ZEROBYTES);
438 439 440
    if (rc == 0)
        state = connected;

441 442 443
    return rc;
}

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
int zmq::curve_client_t::process_error (
        const uint8_t *msg_data, size_t msg_size)
{
    if (state != expect_welcome && state != expect_ready) {
        errno = EPROTO;
        return -1;
    }
    if (msg_size < 7) {
        errno = EPROTO;
        return -1;
    }
    const size_t error_reason_len = static_cast <size_t> (msg_data [6]);
    if (error_reason_len > msg_size - 7) {
        errno = EPROTO;
        return -1;
    }
    state = error_received;
    return 0;
}

464
#endif