From d9bb16725ebe52faa3655ddd39cf2b8d0c82d0a3 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Thu, 20 Jun 2013 18:09:12 +0200 Subject: [PATCH 1/2] Added options for CURVE security - ZMQ_CURVE_PUBLICKEY for clients and servers - ZMQ_CURVE_SECRETKEY for clients - ZMQ_CURVE_SERVERKEY for clients - ZMQ_CURVE_SERVER for servers - added tools/curve_keygen.c as example - updated man pages --- .gitignore | 2 ++ doc/zmq.txt | 6 ++-- doc/zmq_curve.txt | 41 +++++++++++++++++++----- doc/zmq_setsockopt.txt | 71 +++++++++++++++++++++++++++++++++++++----- include/zmq.h | 9 +++--- src/curve_client.cpp | 11 ++----- src/curve_server.cpp | 3 +- src/options.cpp | 57 +++++++++++++++++++++++++++++++++ src/options.hpp | 15 +++++---- tools/curve_keygen.c | 47 ++++++++++++++++++++++++++++ 10 files changed, 222 insertions(+), 40 deletions(-) create mode 100644 tools/curve_keygen.c diff --git a/.gitignore b/.gitignore index ce586a13..be7d2e75 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ autom4te.cache .* *~ .*~ +tools/curve_keygen.o +tools/curve_keygen tests/test_term_endpoint tests/test_monitor tests/test_last_endpoint diff --git a/doc/zmq.txt b/doc/zmq.txt index bbdcfb50..0dbf6d67 100644 --- a/doc/zmq.txt +++ b/doc/zmq.txt @@ -187,10 +187,10 @@ The following security mechanisms are provided for IPC and TCP connections: Null security:: linkzmq:zmq_null[7] -Clear-text authentication using username and password:: - linkzmq:zmq_clear[7] +Plain-text authentication using username and password:: + linkzmq:zmq_plain[7] -Secure authentication and encryption:: +Elliptic curve authentication and encryption:: linkzmq:zmq_curve[7] diff --git a/doc/zmq_curve.txt b/doc/zmq_curve.txt index da2bcfd4..e865bd68 100644 --- a/doc/zmq_curve.txt +++ b/doc/zmq_curve.txt @@ -4,7 +4,7 @@ zmq_curve(7) NAME ---- -zmq_curve - clear-text authentication +zmq_curve - secure authentication and confidentiality SYNOPSIS @@ -15,16 +15,41 @@ is intended for use on public networks. The CURVE mechanism is defined by this document: . -SERVER AND CLIENT ROLES +CLIENT AND SERVER ROLES ----------------------- -To use CURVE, the server shall set the ZMQ_CURVE_SERVER option, and the -client shall set the ZMQ_CURVE_PUBLICKEY and ZMQ_CURVE_SERVERKEY socket -options. Which peer binds, and which connects, is not relevant. +A socket using CURVE can be either client or server but not both. The role +is independent of bind/connect direction. -NOTE: this isn't implemented and not fully defined. The server keypair -needs to be persistent, and it would be sensible to define a format for -this as a CurveZMQ RFC. +To become a CURVE server, the application sets the ZMQ_CURVE_SERVER option +on the socket, and then sets the ZMQ_CURVE_SECRETKEY option to provide the +socket with its long-term secret key. The application does not provide the +socket with its long-term public key, which is used only by clients. +To become a CURVE client, the application sets the ZMQ_CURVE_SERVERKEY +option with the long-term public key of the server it intends to connect +to. A CURVE client can connect to (or accept connections from) at most +one CURVE server. The application then sets the ZMQ_CURVE_PUBLICKEY and +ZMQ_CURVE_SECRETKEY options with its client long-term key pair. + +If the server does authentication it will be based on the client's long +term public key. + +TEST KEY VALUES +--------------- + +For test cases, the client shall use this long-term key pair: + +---- +public: BB88471D65E2659B30C55A5321CEBB5AAB2B70A398645C26DCA2B2FCB43FC518 +secret: 7BB864B489AFA3671FBE69101F94B38972F24816DFB01B51656B3FEC8DFD0888 +---- + +And the server shall use this long-term key pair: + +---- +public: 54FCBA24E93249969316FB617C872BB0C1D1FF14800427C594CBFACF1BC2D652 +secret: 8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7 +---- SEE ALSO -------- diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 3eecec6e..f2d76bdf 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -528,7 +528,7 @@ ZMQ_PLAIN_SERVER: Set PLAIN server role ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines whether the socket will act as server for PLAIN security, see -linkzmq:zmq_plain[3]. A value of '1' means the socket will act as +linkzmq:zmq_plain[7]. A value of '1' means the socket will act as PLAIN server. A value of '0' means the socket will not act as PLAIN server, and its security role then depends on other option settings. Setting this to '0' shall reset the socket security to NULL. @@ -537,7 +537,7 @@ Setting this to '0' shall reset the socket security to NULL. Option value type:: int Option value unit:: 0, 1 Default value:: 0 -Applicable socket types:: all, when using TCP or IPC transports +Applicable socket types:: all, when using TCP transport ZMQ_PLAIN_USERNAME: Set PLAIN security username @@ -545,14 +545,14 @@ ZMQ_PLAIN_USERNAME: Set PLAIN security username Sets the username for outgoing connections over TCP or IPC. If you set this to a non-null value, the security mechanism used for connections shall be -PLAIN, see linkzmq:zmq_plain[3]. If you set this to a null value, the security +PLAIN, see linkzmq:zmq_plain[7]. If you set this to a null value, the security mechanism used for connections shall be NULL, see linkzmq:zmq_null[3]. [horizontal] Option value type:: character string Option value unit:: N/A Default value:: not set -Applicable socket types:: all, when using TCP or IPC transports +Applicable socket types:: all, when using TCP transport ZMQ_PLAIN_PASSWORD: Set PLAIN security password @@ -567,7 +567,66 @@ mechanism used for connections shall be NULL, see linkzmq:zmq_null[3]. Option value type:: character string Option value unit:: N/A Default value:: not set -Applicable socket types:: all, when using TCP or IPC transports +Applicable socket types:: all, when using TCP transport + + +ZMQ_CURVE_SERVER: Set CURVE server role +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Defines whether the socket will act as server for CURVE security, see +linkzmq:zmq_curve[7]. A value of '1' means the socket will act as +CURVE server. A value of '0' means the socket will not act as CURVE +server, and its security role then depends on other option settings. +Setting this to '0' shall reset the socket security to NULL. When you +set this you must also set the ZMQ_CURVE_PUBLICKEY option. + +[horizontal] +Option value type:: int +Option value unit:: 0, 1 +Default value:: 0 +Applicable socket types:: all, when using TCP transport + + +ZMQ_CURVE_PUBLICKEY: Set CURVE public key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the socket's long term public key. You must set this on a CURVE +client or server socket, see linkzmq:zmq_curve[7]. The key is 32 bytes. +For servers, it must be persisted and shared through some unspecified +secure mechanism to clients. + +[horizontal] +Option value type:: binary data +Option value size:: 32 +Default value:: NULL +Applicable socket types:: all, when using TCP transport + + +ZMQ_CURVE_SECRETKEY: Set CURVE secret key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the socket's long term secret key. You must set this on a CURVE +client socket, see linkzmq:zmq_curve[7]. The key is 32 bytes. + +[horizontal] +Option value type:: binary data +Option value size:: 32 +Default value:: NULL +Applicable socket types:: all, when using TCP transport + + +ZMQ_CURVE_SERVERKEY: Set CURVE server key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the socket's long term server key. You must set this on a CURVE +client socket, see linkzmq:zmq_curve[7]. The key is 32 bytes. This +key must be the same as the public key set on the server socket. + +[horizontal] +Option value type:: binary data +Option value size:: 32 +Default value:: NULL +Applicable socket types:: all, when using TCP transport RETURN VALUE @@ -630,5 +689,3 @@ AUTHORS ------- This page was written by the 0MQ community. To make a change please read the 0MQ Contribution Policy at . - - diff --git a/include/zmq.h b/include/zmq.h index 475f86dd..536aa9fa 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -255,7 +255,6 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); #define ZMQ_MULTICAST_HOPS 25 #define ZMQ_RCVTIMEO 27 #define ZMQ_SNDTIMEO 28 -#define ZMQ_IPV4ONLY 31 /* Request replacement by IPV6 */ #define ZMQ_LAST_ENDPOINT 32 #define ZMQ_ROUTER_MANDATORY 33 #define ZMQ_TCP_KEEPALIVE 34 @@ -273,8 +272,9 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); #define ZMQ_PLAIN_PASSWORD 46 #define ZMQ_CURVE_SERVER 47 #define ZMQ_CURVE_PUBLICKEY 48 -#define ZMQ_CURVE_SERVERKEY 49 -#define ZMQ_PROBE_ROUTER 50 +#define ZMQ_CURVE_SECRETKEY 49 +#define ZMQ_CURVE_SERVERKEY 50 +#define ZMQ_PROBE_ROUTER 51 /* Message options */ #define ZMQ_MORE 1 @@ -288,7 +288,8 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); #define ZMQ_PLAIN 1 #define ZMQ_CURVE 2 -/* Deprecated aliases */ +/* Deprecated options and aliases */ +#define ZMQ_IPV4ONLY 31 #define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE #define ZMQ_NOBLOCK ZMQ_DONTWAIT #define ZMQ_FAIL_UNROUTABLE ZMQ_ROUTER_MANDATORY diff --git a/src/curve_client.cpp b/src/curve_client.cpp index 6a4851ef..40dd6b0b 100644 --- a/src/curve_client.cpp +++ b/src/curve_client.cpp @@ -37,14 +37,9 @@ zmq::curve_client_t::curve_client_t (const options_t &options_) : mechanism_t (options_), state (send_hello) { - zmq_assert (options_.public_key_size == crypto_box_PUBLICKEYBYTES); - memcpy (public_key, options_.public_key, crypto_box_PUBLICKEYBYTES); - - zmq_assert (options_.secret_key_size == crypto_box_SECRETKEYBYTES); - memcpy (secret_key, options_.secret_key, crypto_box_SECRETKEYBYTES); - - zmq_assert (options_.server_key_size == crypto_box_PUBLICKEYBYTES); - memcpy (server_key, options_.server_key, crypto_box_PUBLICKEYBYTES); + 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); // Generate short-term key pair const int rc = crypto_box_keypair (cn_public, cn_secret); diff --git a/src/curve_server.cpp b/src/curve_server.cpp index 96c3e996..a60b4d8e 100644 --- a/src/curve_server.cpp +++ b/src/curve_server.cpp @@ -40,8 +40,7 @@ zmq::curve_server_t::curve_server_t (session_base_t *session_, cn_nonce (1) { // Fetch our secret key from socket options - zmq_assert (options_.secret_key_size == crypto_box_SECRETKEYBYTES); - memcpy (secret_key, options_.secret_key, crypto_box_SECRETKEYBYTES); + memcpy (secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES); // Generate short-term key pair const int rc = crypto_box_keypair (cn_public, cn_secret); diff --git a/src/options.cpp b/src/options.cpp index 31401651..bd305a47 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -285,6 +285,35 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, } break; + case ZMQ_CURVE_SERVER: + if (is_int && (value == 0 || value == 1)) { + as_server = value; + mechanism = value? ZMQ_CURVE: ZMQ_NULL; + return 0; + } + break; + + case ZMQ_CURVE_PUBLICKEY: + if (optvallen_ == CURVE_KEYSIZE) { + memcpy (curve_public_key, optval_, CURVE_KEYSIZE); + return 0; + } + break; + + case ZMQ_CURVE_SECRETKEY: + if (optvallen_ == CURVE_KEYSIZE) { + memcpy (curve_secret_key, optval_, CURVE_KEYSIZE); + return 0; + } + break; + + case ZMQ_CURVE_SERVERKEY: + if (optvallen_ == CURVE_KEYSIZE) { + memcpy (curve_server_key, optval_, CURVE_KEYSIZE); + return 0; + } + break; + default: break; } @@ -505,6 +534,34 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) return 0; } break; + + case ZMQ_CURVE_SERVER: + if (is_int) { + *value = as_server && mechanism == ZMQ_CURVE; + return 0; + } + break; + + case ZMQ_CURVE_PUBLICKEY: + if (*optvallen_ == CURVE_KEYSIZE) { + memcpy (optval_, curve_public_key, CURVE_KEYSIZE); + return 0; + } + break; + + case ZMQ_CURVE_SECRETKEY: + if (*optvallen_ == CURVE_KEYSIZE) { + memcpy (optval_, curve_secret_key, CURVE_KEYSIZE); + return 0; + } + break; + + case ZMQ_CURVE_SERVERKEY: + if (*optvallen_ == CURVE_KEYSIZE) { + memcpy (optval_, curve_server_key, CURVE_KEYSIZE); + return 0; + } + break; } errno = EINVAL; return -1; diff --git a/src/options.hpp b/src/options.hpp index 29d38787..1b706b71 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -28,6 +28,8 @@ #include "tcp_address.hpp" #include "../include/zmq.h" +#define CURVE_KEYSIZE 32 + namespace zmq { struct options_t @@ -125,6 +127,7 @@ namespace zmq // Security mechanism for all connections on this socket int mechanism; + // If peer is acting as server for PLAIN or CURVE mechanisms int as_server; @@ -132,14 +135,10 @@ namespace zmq std::string plain_username; std::string plain_password; - unsigned char public_key_size; - unsigned char public_key [32]; - - unsigned char secret_key_size; - unsigned char secret_key [32]; - - unsigned char server_key_size; - unsigned char server_key [32]; + // Security credentials for CURVE mechanism + uint8_t curve_public_key [CURVE_KEYSIZE]; + uint8_t curve_secret_key [CURVE_KEYSIZE]; + uint8_t curve_server_key [CURVE_KEYSIZE]; // ID of the socket. int socket_id; diff --git a/tools/curve_keygen.c b/tools/curve_keygen.c new file mode 100644 index 00000000..39bd21e8 --- /dev/null +++ b/tools/curve_keygen.c @@ -0,0 +1,47 @@ +/* + Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ 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. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include + +int main (void) +{ +# if crypto_box_PUBLICKEYBYTES != 32 \ + || crypto_box_SECRETKEYBYTES != 32 +# error "libsodium not built correctly" +# endif + + uint8_t public_key [32]; + uint8_t secret_key [32]; + + int rc = crypto_box_keypair (public_key, secret_key); + assert (rc == 0); + int byte_nbr; + printf ("public: "); + for (byte_nbr = 0; byte_nbr < 32; byte_nbr++) + printf ("%02X", public_key [byte_nbr]); + printf ("\n"); + printf ("secret: "); + for (byte_nbr = 0; byte_nbr < 32; byte_nbr++) + printf ("%02X", secret_key [byte_nbr]); + printf ("\n"); + exit (0); +} From 10f69c1500af39e7405b8a07b16be11c01827d70 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Thu, 20 Jun 2013 18:30:30 +0200 Subject: [PATCH 2/2] Clarified man pages on mixed security --- doc/zmq_curve.txt | 13 ++++++++----- doc/zmq_setsockopt.txt | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/doc/zmq_curve.txt b/doc/zmq_curve.txt index e865bd68..a414dfa4 100644 --- a/doc/zmq_curve.txt +++ b/doc/zmq_curve.txt @@ -17,8 +17,11 @@ by this document: . CLIENT AND SERVER ROLES ----------------------- -A socket using CURVE can be either client or server but not both. The role -is independent of bind/connect direction. +A socket using CURVE can be either client or server, at any moment, but +not both. The role is independent of bind/connect direction. + +A socket can change roles at any point by setting new options. The role +affects all zmq_connect and zmq_bind calls that follow it. To become a CURVE server, the application sets the ZMQ_CURVE_SERVER option on the socket, and then sets the ZMQ_CURVE_SECRETKEY option to provide the @@ -27,9 +30,9 @@ socket with its long-term public key, which is used only by clients. To become a CURVE client, the application sets the ZMQ_CURVE_SERVERKEY option with the long-term public key of the server it intends to connect -to. A CURVE client can connect to (or accept connections from) at most -one CURVE server. The application then sets the ZMQ_CURVE_PUBLICKEY and -ZMQ_CURVE_SECRETKEY options with its client long-term key pair. +to, or accept connections from, next. The application then sets the +ZMQ_CURVE_PUBLICKEY and ZMQ_CURVE_SECRETKEY options with its client +long-term key pair. If the server does authentication it will be based on the client's long term public key. diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index f2d76bdf..92306596 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -13,8 +13,10 @@ SYNOPSIS *int zmq_setsockopt (void '*socket', int 'option_name', const void '*option_value', size_t 'option_len');* Caution: All options, with the exception of ZMQ_SUBSCRIBE, ZMQ_UNSUBSCRIBE, -ZMQ_LINGER, ZMQ_ROUTER_MANDATORY, ZMQ_PROBE_ROUTER, and ZMQ_XPUB_VERBOSE -only take effect for subsequent socket bind/connects. +ZMQ_LINGER, ZMQ_ROUTER_MANDATORY, ZMQ_PROBE_ROUTER, ZMQ_XPUB_VERBOSE only +take effect for subsequent socket bind/connects. Specifically, security +options take effect for subsequent binds/connects and can be changed at any +time to affect subsequent binds and/or connects. DESCRIPTION -----------