| 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 | |
| 33 | #ifdef ZMQ_HAVE_CURVE |
| 34 | |
| 35 | #include "msg.hpp" |
| 36 | #include "session_base.hpp" |
| 37 | #include "err.hpp" |
| 38 | #include "curve_server.hpp" |
| 39 | #include "wire.hpp" |
| 40 | #include "secure_allocator.hpp" |
| 41 | |
| 42 | zmq::curve_server_t::curve_server_t (session_base_t *session_, |
| 43 | const std::string &peer_address_, |
| 44 | const options_t &options_) : |
| 45 | mechanism_base_t (session_, options_), |
| 46 | zap_client_common_handshake_t ( |
| 47 | session_, peer_address_, options_, sending_ready), |
| 48 | curve_mechanism_base_t ( |
| 49 | session_, options_, "CurveZMQMESSAGES" , "CurveZMQMESSAGEC" ) |
| 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 | zmq_assert (rc == 0); |
| 58 | } |
| 59 | |
| 60 | zmq::curve_server_t::~curve_server_t () |
| 61 | { |
| 62 | } |
| 63 | |
| 64 | int zmq::curve_server_t::next_handshake_command (msg_t *msg_) |
| 65 | { |
| 66 | int rc = 0; |
| 67 | |
| 68 | switch (state) { |
| 69 | case sending_welcome: |
| 70 | rc = produce_welcome (msg_); |
| 71 | if (rc == 0) |
| 72 | state = waiting_for_initiate; |
| 73 | break; |
| 74 | case sending_ready: |
| 75 | rc = produce_ready (msg_); |
| 76 | if (rc == 0) |
| 77 | state = ready; |
| 78 | break; |
| 79 | case sending_error: |
| 80 | rc = produce_error (msg_); |
| 81 | if (rc == 0) |
| 82 | state = error_sent; |
| 83 | break; |
| 84 | default: |
| 85 | errno = EAGAIN; |
| 86 | rc = -1; |
| 87 | break; |
| 88 | } |
| 89 | return rc; |
| 90 | } |
| 91 | |
| 92 | int zmq::curve_server_t::process_handshake_command (msg_t *msg_) |
| 93 | { |
| 94 | int rc = 0; |
| 95 | |
| 96 | switch (state) { |
| 97 | case waiting_for_hello: |
| 98 | rc = process_hello (msg_); |
| 99 | break; |
| 100 | case waiting_for_initiate: |
| 101 | rc = process_initiate (msg_); |
| 102 | break; |
| 103 | default: |
| 104 | // TODO I think this is not a case reachable with a misbehaving |
| 105 | // client. It is not an "invalid handshake command", but would be |
| 106 | // trying to process a handshake command in an invalid state, |
| 107 | // which is purely under control of this peer. |
| 108 | // Therefore, it should be changed to zmq_assert (false); |
| 109 | |
| 110 | // CURVE I: invalid handshake command |
| 111 | session->get_socket ()->event_handshake_failed_protocol ( |
| 112 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED); |
| 113 | errno = EPROTO; |
| 114 | rc = -1; |
| 115 | break; |
| 116 | } |
| 117 | if (rc == 0) { |
| 118 | rc = msg_->close (); |
| 119 | errno_assert (rc == 0); |
| 120 | rc = msg_->init (); |
| 121 | errno_assert (rc == 0); |
| 122 | } |
| 123 | return rc; |
| 124 | } |
| 125 | |
| 126 | int zmq::curve_server_t::encode (msg_t *msg_) |
| 127 | { |
| 128 | zmq_assert (state == ready); |
| 129 | return curve_mechanism_base_t::encode (msg_); |
| 130 | } |
| 131 | |
| 132 | int zmq::curve_server_t::decode (msg_t *msg_) |
| 133 | { |
| 134 | zmq_assert (state == ready); |
| 135 | return curve_mechanism_base_t::decode (msg_); |
| 136 | } |
| 137 | |
| 138 | int zmq::curve_server_t::process_hello (msg_t *msg_) |
| 139 | { |
| 140 | int rc = check_basic_command_structure (msg_); |
| 141 | if (rc == -1) |
| 142 | return -1; |
| 143 | |
| 144 | const size_t size = msg_->size (); |
| 145 | const uint8_t *const hello = static_cast<uint8_t *> (msg_->data ()); |
| 146 | |
| 147 | if (size < 6 || memcmp (hello, "\x05HELLO" , 6)) { |
| 148 | session->get_socket ()->event_handshake_failed_protocol ( |
| 149 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); |
| 150 | errno = EPROTO; |
| 151 | return -1; |
| 152 | } |
| 153 | |
| 154 | if (size != 200) { |
| 155 | session->get_socket ()->event_handshake_failed_protocol ( |
| 156 | session->get_endpoint (), |
| 157 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); |
| 158 | errno = EPROTO; |
| 159 | return -1; |
| 160 | } |
| 161 | |
| 162 | const uint8_t major = hello[6]; |
| 163 | const uint8_t minor = hello[7]; |
| 164 | |
| 165 | if (major != 1 || minor != 0) { |
| 166 | // CURVE I: client HELLO has unknown version number |
| 167 | session->get_socket ()->event_handshake_failed_protocol ( |
| 168 | session->get_endpoint (), |
| 169 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); |
| 170 | errno = EPROTO; |
| 171 | return -1; |
| 172 | } |
| 173 | |
| 174 | // Save client's short-term public key (C') |
| 175 | memcpy (_cn_client, hello + 80, 32); |
| 176 | |
| 177 | uint8_t hello_nonce[crypto_box_NONCEBYTES]; |
| 178 | std::vector<uint8_t, secure_allocator_t<uint8_t> > hello_plaintext ( |
| 179 | crypto_box_ZEROBYTES + 64); |
| 180 | uint8_t hello_box[crypto_box_BOXZEROBYTES + 80]; |
| 181 | |
| 182 | memcpy (hello_nonce, "CurveZMQHELLO---" , 16); |
| 183 | memcpy (hello_nonce + 16, hello + 112, 8); |
| 184 | cn_peer_nonce = get_uint64 (hello + 112); |
| 185 | |
| 186 | memset (hello_box, 0, crypto_box_BOXZEROBYTES); |
| 187 | memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80); |
| 188 | |
| 189 | // Open Box [64 * %x0](C'->S) |
| 190 | rc = crypto_box_open (&hello_plaintext[0], hello_box, sizeof hello_box, |
| 191 | hello_nonce, _cn_client, _secret_key); |
| 192 | if (rc != 0) { |
| 193 | // CURVE I: cannot open client HELLO -- wrong server key? |
| 194 | session->get_socket ()->event_handshake_failed_protocol ( |
| 195 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
| 196 | errno = EPROTO; |
| 197 | return -1; |
| 198 | } |
| 199 | |
| 200 | state = sending_welcome; |
| 201 | return rc; |
| 202 | } |
| 203 | |
| 204 | int zmq::curve_server_t::produce_welcome (msg_t *msg_) |
| 205 | { |
| 206 | uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; |
| 207 | std::vector<uint8_t, secure_allocator_t<uint8_t> > cookie_plaintext ( |
| 208 | crypto_secretbox_ZEROBYTES + 64); |
| 209 | uint8_t cookie_ciphertext[crypto_secretbox_BOXZEROBYTES + 80]; |
| 210 | |
| 211 | // Create full nonce for encryption |
| 212 | // 8-byte prefix plus 16-byte random nonce |
| 213 | memcpy (cookie_nonce, "COOKIE--" , 8); |
| 214 | randombytes (cookie_nonce + 8, 16); |
| 215 | |
| 216 | // Generate cookie = Box [C' + s'](t) |
| 217 | std::fill (cookie_plaintext.begin (), |
| 218 | cookie_plaintext.begin () + crypto_secretbox_ZEROBYTES, 0); |
| 219 | memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES], _cn_client, 32); |
| 220 | memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES + 32], _cn_secret, 32); |
| 221 | |
| 222 | // Generate fresh cookie key |
| 223 | randombytes (_cookie_key, crypto_secretbox_KEYBYTES); |
| 224 | |
| 225 | // Encrypt using symmetric cookie key |
| 226 | int rc = |
| 227 | crypto_secretbox (cookie_ciphertext, &cookie_plaintext[0], |
| 228 | cookie_plaintext.size (), cookie_nonce, _cookie_key); |
| 229 | zmq_assert (rc == 0); |
| 230 | |
| 231 | uint8_t welcome_nonce[crypto_box_NONCEBYTES]; |
| 232 | std::vector<uint8_t, secure_allocator_t<uint8_t> > welcome_plaintext ( |
| 233 | crypto_box_ZEROBYTES + 128); |
| 234 | uint8_t welcome_ciphertext[crypto_box_BOXZEROBYTES + 144]; |
| 235 | |
| 236 | // Create full nonce for encryption |
| 237 | // 8-byte prefix plus 16-byte random nonce |
| 238 | memcpy (welcome_nonce, "WELCOME-" , 8); |
| 239 | randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8); |
| 240 | |
| 241 | // Create 144-byte Box [S' + cookie](S->C') |
| 242 | std::fill (welcome_plaintext.begin (), |
| 243 | welcome_plaintext.begin () + crypto_box_ZEROBYTES, 0); |
| 244 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES], _cn_public, 32); |
| 245 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 32], cookie_nonce + 8, |
| 246 | 16); |
| 247 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 48], |
| 248 | cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80); |
| 249 | |
| 250 | rc = crypto_box (welcome_ciphertext, &welcome_plaintext[0], |
| 251 | welcome_plaintext.size (), welcome_nonce, _cn_client, |
| 252 | _secret_key); |
| 253 | |
| 254 | // TODO I think we should change this back to zmq_assert (rc == 0); |
| 255 | // as it was before https://github.com/zeromq/libzmq/pull/1832 |
| 256 | // The reason given there was that secret_key might be 0ed. |
| 257 | // But if it were, we would never get this far, since we could |
| 258 | // not have opened the client's hello box with a 0ed key. |
| 259 | |
| 260 | if (rc == -1) |
| 261 | return -1; |
| 262 | |
| 263 | rc = msg_->init_size (168); |
| 264 | errno_assert (rc == 0); |
| 265 | |
| 266 | uint8_t *const welcome = static_cast<uint8_t *> (msg_->data ()); |
| 267 | memcpy (welcome, "\x07WELCOME" , 8); |
| 268 | memcpy (welcome + 8, welcome_nonce + 8, 16); |
| 269 | memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144); |
| 270 | |
| 271 | return 0; |
| 272 | } |
| 273 | |
| 274 | int zmq::curve_server_t::process_initiate (msg_t *msg_) |
| 275 | { |
| 276 | int rc = check_basic_command_structure (msg_); |
| 277 | if (rc == -1) |
| 278 | return -1; |
| 279 | |
| 280 | const size_t size = msg_->size (); |
| 281 | const uint8_t *initiate = static_cast<uint8_t *> (msg_->data ()); |
| 282 | |
| 283 | if (size < 9 || memcmp (initiate, "\x08INITIATE" , 9)) { |
| 284 | session->get_socket ()->event_handshake_failed_protocol ( |
| 285 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); |
| 286 | errno = EPROTO; |
| 287 | return -1; |
| 288 | } |
| 289 | |
| 290 | if (size < 257) { |
| 291 | session->get_socket ()->event_handshake_failed_protocol ( |
| 292 | session->get_endpoint (), |
| 293 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); |
| 294 | errno = EPROTO; |
| 295 | return -1; |
| 296 | } |
| 297 | |
| 298 | uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; |
| 299 | uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64]; |
| 300 | uint8_t cookie_box[crypto_secretbox_BOXZEROBYTES + 80]; |
| 301 | |
| 302 | // Open Box [C' + s'](t) |
| 303 | memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES); |
| 304 | memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80); |
| 305 | |
| 306 | memcpy (cookie_nonce, "COOKIE--" , 8); |
| 307 | memcpy (cookie_nonce + 8, initiate + 9, 16); |
| 308 | |
| 309 | rc = crypto_secretbox_open (cookie_plaintext, cookie_box, sizeof cookie_box, |
| 310 | cookie_nonce, _cookie_key); |
| 311 | if (rc != 0) { |
| 312 | // CURVE I: cannot open client INITIATE cookie |
| 313 | session->get_socket ()->event_handshake_failed_protocol ( |
| 314 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
| 315 | errno = EPROTO; |
| 316 | return -1; |
| 317 | } |
| 318 | |
| 319 | // Check cookie plain text is as expected [C' + s'] |
| 320 | if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32) |
| 321 | || memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32, |
| 322 | _cn_secret, 32)) { |
| 323 | // TODO this case is very hard to test, as it would require a modified |
| 324 | // client that knows the server's secret temporary cookie key |
| 325 | |
| 326 | // CURVE I: client INITIATE cookie is not valid |
| 327 | session->get_socket ()->event_handshake_failed_protocol ( |
| 328 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
| 329 | errno = EPROTO; |
| 330 | return -1; |
| 331 | } |
| 332 | |
| 333 | const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES; |
| 334 | |
| 335 | uint8_t initiate_nonce[crypto_box_NONCEBYTES]; |
| 336 | std::vector<uint8_t, secure_allocator_t<uint8_t> > initiate_plaintext ( |
| 337 | crypto_box_ZEROBYTES + clen); |
| 338 | std::vector<uint8_t> initiate_box (crypto_box_BOXZEROBYTES + clen); |
| 339 | |
| 340 | // Open Box [C + vouch + metadata](C'->S') |
| 341 | std::fill (initiate_box.begin (), |
| 342 | initiate_box.begin () + crypto_box_BOXZEROBYTES, 0); |
| 343 | memcpy (&initiate_box[crypto_box_BOXZEROBYTES], initiate + 113, |
| 344 | clen - crypto_box_BOXZEROBYTES); |
| 345 | |
| 346 | memcpy (initiate_nonce, "CurveZMQINITIATE" , 16); |
| 347 | memcpy (initiate_nonce + 16, initiate + 105, 8); |
| 348 | cn_peer_nonce = get_uint64 (initiate + 105); |
| 349 | |
| 350 | const uint8_t *client_key = &initiate_plaintext[crypto_box_ZEROBYTES]; |
| 351 | |
| 352 | rc = crypto_box_open (&initiate_plaintext[0], &initiate_box[0], clen, |
| 353 | initiate_nonce, _cn_client, _cn_secret); |
| 354 | if (rc != 0) { |
| 355 | // CURVE I: cannot open client INITIATE |
| 356 | session->get_socket ()->event_handshake_failed_protocol ( |
| 357 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
| 358 | errno = EPROTO; |
| 359 | return -1; |
| 360 | } |
| 361 | |
| 362 | uint8_t vouch_nonce[crypto_box_NONCEBYTES]; |
| 363 | std::vector<uint8_t, secure_allocator_t<uint8_t> > vouch_plaintext ( |
| 364 | crypto_box_ZEROBYTES + 64); |
| 365 | uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80]; |
| 366 | |
| 367 | // Open Box Box [C',S](C->S') and check contents |
| 368 | memset (vouch_box, 0, crypto_box_BOXZEROBYTES); |
| 369 | memcpy (vouch_box + crypto_box_BOXZEROBYTES, |
| 370 | &initiate_plaintext[crypto_box_ZEROBYTES + 48], 80); |
| 371 | |
| 372 | memcpy (vouch_nonce, "VOUCH---" , 8); |
| 373 | memcpy (vouch_nonce + 8, &initiate_plaintext[crypto_box_ZEROBYTES + 32], |
| 374 | 16); |
| 375 | |
| 376 | rc = crypto_box_open (&vouch_plaintext[0], vouch_box, sizeof vouch_box, |
| 377 | vouch_nonce, client_key, _cn_secret); |
| 378 | if (rc != 0) { |
| 379 | // CURVE I: cannot open client INITIATE vouch |
| 380 | session->get_socket ()->event_handshake_failed_protocol ( |
| 381 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
| 382 | errno = EPROTO; |
| 383 | return -1; |
| 384 | } |
| 385 | |
| 386 | // What we decrypted must be the client's short-term public key |
| 387 | if (memcmp (&vouch_plaintext[crypto_box_ZEROBYTES], _cn_client, 32)) { |
| 388 | // TODO this case is very hard to test, as it would require a modified |
| 389 | // client that knows the server's secret short-term key |
| 390 | |
| 391 | // CURVE I: invalid handshake from client (public key) |
| 392 | session->get_socket ()->event_handshake_failed_protocol ( |
| 393 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE); |
| 394 | errno = EPROTO; |
| 395 | return -1; |
| 396 | } |
| 397 | |
| 398 | // Precompute connection secret from client key |
| 399 | rc = crypto_box_beforenm (cn_precom, _cn_client, _cn_secret); |
| 400 | zmq_assert (rc == 0); |
| 401 | |
| 402 | // Given this is a backward-incompatible change, it's behind a socket |
| 403 | // option disabled by default. |
| 404 | if (zap_required () || !options.zap_enforce_domain) { |
| 405 | // Use ZAP protocol (RFC 27) to authenticate the user. |
| 406 | rc = session->zap_connect (); |
| 407 | if (rc == 0) { |
| 408 | send_zap_request (client_key); |
| 409 | state = waiting_for_zap_reply; |
| 410 | |
| 411 | // TODO actually, it is quite unlikely that we can read the ZAP |
| 412 | // reply already, but removing this has some strange side-effect |
| 413 | // (probably because the pipe's in_active flag is true until a read |
| 414 | // is attempted) |
| 415 | if (-1 == receive_and_process_zap_reply ()) |
| 416 | return -1; |
| 417 | } else if (!options.zap_enforce_domain) { |
| 418 | // This supports the Stonehouse pattern (encryption without |
| 419 | // authentication) in legacy mode (domain set but no handler). |
| 420 | state = sending_ready; |
| 421 | } else { |
| 422 | session->get_socket ()->event_handshake_failed_no_detail ( |
| 423 | session->get_endpoint (), EFAULT); |
| 424 | return -1; |
| 425 | } |
| 426 | } else { |
| 427 | // This supports the Stonehouse pattern (encryption without authentication). |
| 428 | state = sending_ready; |
| 429 | } |
| 430 | |
| 431 | return parse_metadata (&initiate_plaintext[crypto_box_ZEROBYTES + 128], |
| 432 | clen - crypto_box_ZEROBYTES - 128); |
| 433 | } |
| 434 | |
| 435 | int zmq::curve_server_t::produce_ready (msg_t *msg_) |
| 436 | { |
| 437 | const size_t metadata_length = basic_properties_len (); |
| 438 | uint8_t ready_nonce[crypto_box_NONCEBYTES]; |
| 439 | |
| 440 | std::vector<uint8_t, secure_allocator_t<uint8_t> > ready_plaintext ( |
| 441 | crypto_box_ZEROBYTES + metadata_length); |
| 442 | |
| 443 | // Create Box [metadata](S'->C') |
| 444 | std::fill (ready_plaintext.begin (), |
| 445 | ready_plaintext.begin () + crypto_box_ZEROBYTES, 0); |
| 446 | uint8_t *ptr = &ready_plaintext[crypto_box_ZEROBYTES]; |
| 447 | |
| 448 | ptr += add_basic_properties (ptr, metadata_length); |
| 449 | const size_t mlen = ptr - &ready_plaintext[0]; |
| 450 | |
| 451 | memcpy (ready_nonce, "CurveZMQREADY---" , 16); |
| 452 | put_uint64 (ready_nonce + 16, cn_nonce); |
| 453 | |
| 454 | std::vector<uint8_t> ready_box (crypto_box_BOXZEROBYTES + 16 |
| 455 | + metadata_length); |
| 456 | |
| 457 | int rc = crypto_box_afternm (&ready_box[0], &ready_plaintext[0], mlen, |
| 458 | ready_nonce, cn_precom); |
| 459 | zmq_assert (rc == 0); |
| 460 | |
| 461 | rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES); |
| 462 | errno_assert (rc == 0); |
| 463 | |
| 464 | uint8_t *ready = static_cast<uint8_t *> (msg_->data ()); |
| 465 | |
| 466 | memcpy (ready, "\x05READY" , 6); |
| 467 | // Short nonce, prefixed by "CurveZMQREADY---" |
| 468 | memcpy (ready + 6, ready_nonce + 16, 8); |
| 469 | // Box [metadata](S'->C') |
| 470 | memcpy (ready + 14, &ready_box[crypto_box_BOXZEROBYTES], |
| 471 | mlen - crypto_box_BOXZEROBYTES); |
| 472 | |
| 473 | cn_nonce++; |
| 474 | |
| 475 | return 0; |
| 476 | } |
| 477 | |
| 478 | int zmq::curve_server_t::produce_error (msg_t *msg_) const |
| 479 | { |
| 480 | const size_t expected_status_code_length = 3; |
| 481 | zmq_assert (status_code.length () == 3); |
| 482 | const int rc = msg_->init_size (6 + 1 + expected_status_code_length); |
| 483 | zmq_assert (rc == 0); |
| 484 | char *msg_data = static_cast<char *> (msg_->data ()); |
| 485 | memcpy (msg_data, "\5ERROR" , 6); |
| 486 | msg_data[6] = expected_status_code_length; |
| 487 | memcpy (msg_data + 7, status_code.c_str (), expected_status_code_length); |
| 488 | return 0; |
| 489 | } |
| 490 | |
| 491 | void zmq::curve_server_t::send_zap_request (const uint8_t *key_) |
| 492 | { |
| 493 | zap_client_t::send_zap_request ("CURVE" , 5, key_, |
| 494 | crypto_box_PUBLICKEYBYTES); |
| 495 | } |
| 496 | |
| 497 | #endif |
| 498 | |