curve_client.cpp 13.9 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 47

#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_),
48
    state (send_hello),
49 50
    cn_nonce(1),
    cn_peer_nonce(1),
51
    sync()
52
{
53
    int rc;
54 55 56
    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);
57
    scoped_lock_t lock (sync);
58
#if defined (ZMQ_USE_TWEETNACL)
59 60 61 62
    // allow opening of /dev/urandom
    unsigned char tmpbytes[4];
    randombytes(tmpbytes, 4);
#else
63 64
    rc = sodium_init ();
    zmq_assert (rc != -1);
65
#endif
66

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

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

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

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

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

104 105 106 107 108 109 110 111 112 113 114 115
    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;
116
    }
117

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

125 126 127
    return rc;
}

128 129 130 131 132 133 134
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
135 136
    if (msg_->flags () & msg_t::command)
        flags |= 0x02;
137 138 139

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

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

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

    free (message_plaintext);
    free (message_box);

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

    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 ());
190
    if (memcmp (message, "\x07MESSAGE", 8)) {
191 192 193 194 195 196 197
        errno = EPROTO;
        return -1;
    }

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

205 206 207 208 209 210 211 212 213 214 215 216 217 218

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

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

    free (message_plaintext);
    free (message_box);

    return rc;
}

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

257
int zmq::curve_client_t::produce_hello (msg_t *msg_)
258 259 260 261 262 263 264
{
    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);
265
    put_uint64 (hello_nonce + 16, cn_nonce);
266 267 268 269 270 271

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

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

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

279
    memcpy (hello, "\x05HELLO", 6);
280
    //  CurveZMQ major and minor version numbers
281
    memcpy (hello + 6, "\1\0", 2);
282
    //  Anti-amplification padding
283
    memset (hello + 8, 0, 72);
284
    //  Client public connection key
285
    memcpy (hello + 80, cn_public, crypto_box_PUBLICKEYBYTES);
286 287 288 289 290
    //  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);

291
    cn_nonce++;
292 293 294 295

    return 0;
}

296 297
int zmq::curve_client_t::process_welcome (
        const uint8_t *msg_data, size_t msg_size)
298
{
299
    if (msg_size != 168) {
300 301 302 303 304 305 306 307 308 309
        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);
310
    memcpy (welcome_box + crypto_box_BOXZEROBYTES, msg_data + 24, 144);
311 312

    memcpy (welcome_nonce, "WELCOME-", 8);
313
    memcpy (welcome_nonce + 8, msg_data + 8, 16);
314 315 316

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

323 324
    memcpy (cn_server, welcome_plaintext + crypto_box_ZEROBYTES, 32);
    memcpy (cn_cookie, welcome_plaintext + crypto_box_ZEROBYTES + 32, 16 + 80);
325 326

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

330 331
    state = send_initiate;

332 333 334
    return 0;
}

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

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

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

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

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

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

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

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

    const size_t mlen = ptr - initiate_plaintext;

    memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
384
    put_uint64 (initiate_nonce + 16, cn_nonce);
385 386

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

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

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

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

    return 0;
}

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

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

    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,
424
            msg_data + 14, clen - crypto_box_BOXZEROBYTES);
425 426

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

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

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

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

443 444 445
    return rc;
}

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
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;
}

466
#endif