Added Z85 support

The use of binary for CURVE keys is painful; you cannot easily copy
these in e.g. email, or use them directly in source code. There are
various encoding possibilities. Base16 and Base64 are not optimal.
Ascii85 is not safe for source (it generates quotes and escapes).

So, I've designed a new Base85 encoding, Z85, which is safe to use
in code and elsewhere, and I've modified libzmq to use this where
it also uses binary keys (in get/setsockopt).

Very simply, if you use a 32-byte value, it's Base256 (binary),
and if you use a 40-byte value, it's Base85 (Z85).

I've put the Z85 codec into z85_codec.hpp, it's not elegant C++
but it is minimal and it works. Feel free to rewrap as a real class
if this annoys you.
This commit is contained in:
Pieter Hintjens 2013-06-28 22:10:22 +02:00
parent 76df045950
commit 7041770108
13 changed files with 417 additions and 82 deletions

View File

@ -1,12 +1,12 @@
ACLOCAL_AMFLAGS = -I config
if BUILD_PGM
SUBDIRS = foreign/openpgm src doc perf tests
SUBDIRS = foreign/openpgm src doc perf tests tools
else
SUBDIRS = src doc perf tests
SUBDIRS = src doc perf tests tools
endif
DIST_SUBDIRS = foreign/openpgm src doc perf tests builds/msvc
DIST_SUBDIRS = foreign/openpgm src doc perf tests tools builds/msvc
EXTRA_DIST = \
autogen.sh \

View File

@ -428,10 +428,14 @@ AC_SUBST(LIBZMQ_EXTRA_CFLAGS)
AC_SUBST(LIBZMQ_EXTRA_CXXFLAGS)
AC_SUBST(LIBZMQ_EXTRA_LDFLAGS)
AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile
perf/Makefile src/libzmq.pc \
builds/msvc/Makefile tests/Makefile \
AC_CONFIG_FILES([Makefile \
src/Makefile \
src/libzmq.pc \
doc/Makefile \
perf/Makefile \
tests/Makefile \
tools/Makefile \
builds/msvc/Makefile \
foreign/openpgm/Makefile \
builds/redhat/zeromq.spec])
AC_OUTPUT

View File

@ -37,21 +37,43 @@ long-term key pair.
If the server does authentication it will be based on the client's long
term public key.
KEY ENCODING
------------
The standard representation for keys in source code is either 32 bytes of
base 256 (binary) data, or 40 characters of base 85 data encoded using the
Z85 algorithm defined by http://rfc.zeromq.org/spec:32.
The Z85 algorithm is designed to produce printable key strings for use in
configuration files, the command line, and code. There is a reference
implementation in C at https://github.com/zeromq/rfc/tree/master/src.
TEST KEY VALUES
---------------
For test cases, the client shall use this long-term key pair:
For test cases, the client shall use this long-term key pair (specified
as hexadecimal and in Z85):
----
public: BB88471D65E2659B30C55A5321CEBB5AAB2B70A398645C26DCA2B2FCB43FC518
secret: 7BB864B489AFA3671FBE69101F94B38972F24816DFB01B51656B3FEC8DFD0888
public:
BB88471D65E2659B30C55A5321CEBB5AAB2B70A398645C26DCA2B2FCB43FC518
Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID
secret:
7BB864B489AFA3671FBE69101F94B38972F24816DFB01B51656B3FEC8DFD0888
D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs
----
And the server shall use this long-term key pair:
And the server shall use this long-term key pair (specified as hexadecimal
and in Z85):
----
public: 54FCBA24E93249969316FB617C872BB0C1D1FF14800427C594CBFACF1BC2D652
secret: 8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7
public:
54FCBA24E93249969316FB617C872BB0C1D1FF14800427C594CBFACF1BC2D652
rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7
secret:
8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7
JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6
----
SEE ALSO

View File

@ -486,8 +486,8 @@ Default value:: -1 (leave to OS default)
Applicable socket types:: all, when using TCP transports.
ZMQ_MECHANISM: Retrieve the current security mechanism
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZMQ_MECHANISM: Retrieve current security mechanism
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 'ZMQ_MECHANISM' option shall retrieve the current security mechanism
for the socket.
@ -498,8 +498,8 @@ Default value:: ZMQ_NULL
Applicable socket types:: all, when using TCP or IPC transports
ZMQ_PLAIN_SERVER: Retrieve the PLAIN server role
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZMQ_PLAIN_SERVER: Retrieve current PLAIN server role
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Returns the 'ZMQ_PLAIN_SERVER' option, if any, previously set on the socket.
[horizontal]
@ -509,8 +509,8 @@ Default value:: int
Applicable socket types:: all, when using TCP or IPC transports
ZMQ_PLAIN_USERNAME: Retrieve the last username set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZMQ_PLAIN_USERNAME: Retrieve current PLAIN username
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 'ZMQ_PLAIN_USERNAME' option shall retrieve the last username set for
the PLAIN security mechanism. The returned value shall be a NULL-terminated
string and MAY be empty. The returned size SHALL include the terminating
@ -523,8 +523,8 @@ Default value:: null string
Applicable socket types:: all, when using TCP or IPC transports
ZMQ_PLAIN_PASSWORD: Retrieve the last password set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZMQ_PLAIN_PASSWORD: Retrieve current password
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 'ZMQ_PLAIN_PASSWORD' option shall retrieve the last password set for
the PLAIN security mechanism. The returned value shall be a NULL-terminated
string and MAY be empty. The returned size SHALL include the terminating
@ -537,6 +537,48 @@ Default value:: null string
Applicable socket types:: all, when using TCP or IPC transports
ZMQ_CURVE_PUBLICKEY: Retrieve current CURVE public key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Retrieves the current long term public key for the socket. You can
provide either a 32 byte buffer, to retrieve the binary key value, or
a 40 byte buffer, to retrieve the key in a printable Z85 format.
[horizontal]
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: null
Applicable socket types:: all, when using TCP transport
ZMQ_CURVE_SECRETKEY: Retrieve current CURVE secret key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Retrieves the current long term secret key for the socket. You can
provide either a 32 byte buffer, to retrieve the binary key value, or
a 40 byte buffer, to retrieve the key in a printable Z85 format.
[horizontal]
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: null
Applicable socket types:: all, when using TCP transport
ZMQ_CURVE_SERVERKEY: Retrieve current CURVE server key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Retrieves the current server key for the client socket. You can
provide either a 32 byte buffer, to retrieve the binary key value, or
a 40 byte buffer, to retrieve the key in a printable Z85 format.
[horizontal]
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: null
Applicable socket types:: all, when using TCP transport
RETURN VALUE
------------
The _zmq_getsockopt()_ function shall return zero if successful. Otherwise it

View File

@ -596,13 +596,17 @@ 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.
client or server socket, see linkzmq:zmq_curve[7]. You can provide the
key as 32 binary bytes, or as a 40-character string encoded in the Z85
encoding format. For servers, the public key must be persisted and
shared through some unspecified but secure mechanism to clients. The
public key must always be used with the matching secret key generated
at the same time. To generate a public/secret key pair, use the
tools/curve_keygen tool.
[horizontal]
Option value type:: binary data
Option value size:: 32
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: NULL
Applicable socket types:: all, when using TCP transport
@ -611,11 +615,13 @@ 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.
client socket, see linkzmq:zmq_curve[7]. You can provide the key as
32 binary bytes, or as a 40-character string encoded in the Z85 encoding
format.
[horizontal]
Option value type:: binary data
Option value size:: 32
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: NULL
Applicable socket types:: all, when using TCP transport
@ -624,12 +630,14 @@ 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.
client socket, see linkzmq:zmq_curve[7]. You can provide the key as
32 binary bytes, or as a 40-character string encoded in the Z85 encoding
format. 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
Option value type:: binary data or Z85 text string
Option value size:: 32 or 40
Default value:: NULL
Applicable socket types:: all, when using TCP transport

View File

@ -89,6 +89,7 @@ libzmq_la_SOURCES = \
xsub.hpp \
ypipe.hpp \
yqueue.hpp \
z85_codec.hpp \
address.cpp \
clock.cpp \
ctx.cpp \

View File

@ -21,6 +21,7 @@
#include "options.hpp"
#include "err.hpp"
#include "z85_codec.hpp"
zmq::options_t::options_t () :
sndhwm (1000),
@ -285,7 +286,7 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
}
break;
// If libsodium isn't installed, these options provoke EINVAL
// If libsodium isn't installed, these options provoke EINVAL
# ifdef HAVE_LIBSODIUM
case ZMQ_CURVE_SERVER:
if (is_int && (value == 0 || value == 1)) {
@ -301,6 +302,12 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
mechanism = ZMQ_CURVE;
return 0;
}
else
if (optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_decode (curve_public_key, (char *) optval_);
mechanism = ZMQ_CURVE;
return 0;
}
break;
case ZMQ_CURVE_SECRETKEY:
@ -309,6 +316,12 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
mechanism = ZMQ_CURVE;
return 0;
}
else
if (optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_decode (curve_secret_key, (char *) optval_);
mechanism = ZMQ_CURVE;
return 0;
}
break;
case ZMQ_CURVE_SERVERKEY:
@ -318,6 +331,13 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
mechanism = ZMQ_CURVE;
return 0;
}
else
if (optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_decode (curve_server_key, (char *) optval_);
as_server = 0;
mechanism = ZMQ_CURVE;
return 0;
}
break;
# endif
}
@ -539,7 +559,7 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
}
break;
// If libsodium isn't installed, these options provoke EINVAL
// If libsodium isn't installed, these options provoke EINVAL
# ifdef HAVE_LIBSODIUM
case ZMQ_CURVE_SERVER:
if (is_int) {
@ -553,6 +573,11 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
memcpy (optval_, curve_public_key, CURVE_KEYSIZE);
return 0;
}
else
if (*optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_encode ((char *) optval_, curve_public_key, CURVE_KEYSIZE);
return 0;
}
break;
case ZMQ_CURVE_SECRETKEY:
@ -560,6 +585,11 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
memcpy (optval_, curve_secret_key, CURVE_KEYSIZE);
return 0;
}
else
if (*optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_encode ((char *) optval_, curve_secret_key, CURVE_KEYSIZE);
return 0;
}
break;
case ZMQ_CURVE_SERVERKEY:
@ -567,6 +597,11 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
memcpy (optval_, curve_server_key, CURVE_KEYSIZE);
return 0;
}
else
if (*optvallen_ == CURVE_KEYSIZE_Z85) {
Z85_encode ((char *) optval_, curve_server_key, CURVE_KEYSIZE);
return 0;
}
break;
# endif
}

View File

@ -28,7 +28,10 @@
#include "tcp_address.hpp"
#include "../include/zmq.h"
#define CURVE_KEYSIZE 32
// Normal base 256 key is 32 bytes
#define CURVE_KEYSIZE 32
// Key encoded using Z85 is 40 bytes
#define CURVE_KEYSIZE_Z85 40
namespace zmq
{

108
src/z85_codec.hpp Normal file
View File

@ -0,0 +1,108 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding
#ifndef __ZMQ_Z85_CODEC_HPP_INCLUDED__
#define __ZMQ_Z85_CODEC_HPP_INCLUDED__
// Maps base 256 to base 85
static char encoder [85 + 1] = {
"0123456789" "abcdefghij" "klmnopqrst" "uvwxyzABCD"
"EFGHIJKLMN" "OPQRSTUVWX" "YZ.-:+=^!/" "*?&<>()[]{"
"}@%$#"
};
// Maps base 85 to base 256
// We chop off lower 32 and higher 128 ranges
static uint8_t decoder [96] = {
0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
};
// --------------------------------------------------------------------------
// Encode a binary frame as a string; destination string MUST be at least
// size * 5 / 4 bytes long. Returns dest. Size must be a multiple of 4.
char *
Z85_encode (char *dest, uint8_t *data, size_t size)
{
assert (size % 4 == 0);
uint char_nbr = 0;
uint byte_nbr = 0;
uint32_t value = 0;
while (byte_nbr < size) {
// Accumulate value in base 256 (binary)
value = value * 256 + data [byte_nbr++];
if (byte_nbr % 4 == 0) {
// Output value in base 85
uint divisor = 85 * 85 * 85 * 85;
while (divisor) {
dest [char_nbr++] = encoder [value / divisor % 85];
divisor /= 85;
}
value = 0;
}
}
assert (char_nbr == size * 5 / 4);
dest [char_nbr] = 0;
return dest;
}
// --------------------------------------------------------------------------
// Decode an encoded string into a binary frame; dest must be at least
// strlen (string) * 4 / 5 bytes long. Returns dest. strlen (string)
// must be a multiple of 5.
uint8_t *
Z85_decode (uint8_t *dest, char *string)
{
assert (strlen (string) % 5 == 0);
uint byte_nbr = 0;
uint char_nbr = 0;
uint32_t value = 0;
while (char_nbr < strlen (string)) {
// Accumulate value in base 85
value = value * 85 + decoder [(uint8_t) string [char_nbr++] - 32];
if (char_nbr % 5 == 0) {
// Output value in base 256
uint divisor = 256 * 256 * 256;
while (divisor) {
dest [byte_nbr++] = value / divisor % 256;
divisor /= 256;
}
value = 0;
}
}
assert (byte_nbr == strlen (string) * 4 / 5);
return dest;
}
#endif

View File

@ -56,7 +56,7 @@ zap_handler (void *zap)
int main (void)
{
#ifndef HAVE_LIBSODIUM
printf("libsodium not installed, skipping CURVE test\n");
printf ("libsodium not installed, skipping CURVE test\n");
return 0;
#endif
int rc;
@ -75,43 +75,24 @@ int main (void)
void *client = zmq_socket (ctx, ZMQ_DEALER);
assert (client);
// Test keys from the zmq_curve man page
char client_public [] = "Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID";
char client_secret [] = "D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs";
char server_public [] = "rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7";
char server_secret [] = "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6";
as_server = 1;
rc = zmq_setsockopt(server, ZMQ_CURVE_SERVER, &as_server, sizeof(int));
// Test key from the zmq_curve man page.
uint8_t server_secret[32] = {
0x8E, 0x0B, 0xDD, 0x69, 0x76, 0x28, 0xB9, 0x1D, 0x8F, 0x24,
0x55, 0x87, 0xEE, 0x95, 0xC5, 0xB0, 0x4D, 0x48, 0x96, 0x3F,
0x79, 0x25, 0x98, 0x77, 0xB4, 0x9C, 0xD9, 0x06, 0x3A, 0xEA,
0xD3, 0xB7
};
rc = zmq_setsockopt(server, ZMQ_CURVE_SECRETKEY, server_secret, sizeof(server_secret));
assert(rc == 0);
rc = zmq_setsockopt (server, ZMQ_CURVE_SERVER, &as_server, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (server, ZMQ_CURVE_SECRETKEY, server_secret, 40);
assert (rc == 0);
// Test client keys from the zmq_curve man page.
uint8_t server_public[32] = {
0x54, 0xFC, 0xBA, 0x24, 0xE9, 0x32, 0x49, 0x96, 0x93, 0x16,
0xFB, 0x61, 0x7C, 0x87, 0x2B, 0xB0, 0xC1, 0xD1, 0xFF, 0x14,
0x80, 0x04, 0x27, 0xC5, 0x94, 0xCB, 0xFA, 0xCF, 0x1B, 0xC2,
0xD6, 0x52
};
rc = zmq_setsockopt(client, ZMQ_CURVE_SERVERKEY, server_public, sizeof(server_public));
assert(rc == 0);
uint8_t client_public[32] = {
0xBB, 0x88, 0x47, 0x1D, 0x65, 0xE2, 0x65, 0x9B, 0x30, 0xC5,
0x5A, 0x53, 0x21, 0xCE, 0xBB, 0x5A, 0xAB, 0x2B, 0x70, 0xA3,
0x98, 0x64, 0x5C, 0x26, 0xDC, 0xA2, 0xB2, 0xFC, 0xB4, 0x3F,
0xC5, 0x18
};
rc = zmq_setsockopt(client, ZMQ_CURVE_PUBLICKEY, client_public, sizeof(client_public));
assert(rc == 0);
uint8_t client_secret[32] = {
0x7B, 0xB8, 0x64, 0xB4, 0x89, 0xAF, 0xA3, 0x67, 0x1F, 0xBE,
0x69, 0x10, 0x1F, 0x94, 0xB3, 0x89, 0x72, 0xF2, 0x48, 0x16,
0xDF, 0xB0, 0x1B, 0x51, 0x65, 0x6B, 0x3F, 0xEC, 0x8D, 0xFD,
0x08, 0x88
};
rc = zmq_setsockopt(client, ZMQ_CURVE_SECRETKEY, client_secret, sizeof(client_secret));
assert(rc == 0);
rc = zmq_setsockopt (client, ZMQ_CURVE_SERVERKEY, server_public, 40);
assert (rc == 0);
rc = zmq_setsockopt (client, ZMQ_CURVE_PUBLICKEY, client_public, 40);
assert (rc == 0);
rc = zmq_setsockopt (client, ZMQ_CURVE_SECRETKEY, client_secret, 40);
assert (rc == 0);
// Test the client and server both have the right mechanism.
optsize = sizeof (int);

1
tools/Makefile.am Normal file
View File

@ -0,0 +1 @@
EXTRA_DIST = curve_keygen.c z85_codec.h

View File

@ -3,6 +3,16 @@
This file is part of 0MQ.
This tool generates a keypair for the libzmq CURVE security mechanism,
and encodes the keypair to give two printable strings that you can use
in configuration files or source code. The encoding uses Z85, which is
a base-85 format that is described in 0MQ RFC 32, and which has an
implementation in the Z85.c source used by this tool. The keypair
always works with the secret key held by one party and the public key
distributed (securely!) to peers wishing to connect to it. CURVE is
defined by http://rfc.zeromq.org/spec:25. Z85 is defined by
http://rfc.zeromq.org/spec:32.
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
@ -21,6 +31,7 @@
#include <stdlib.h>
#include <assert.h>
#include <sodium.h>
#include "z85_codec.h"
int main (void)
{
@ -28,20 +39,31 @@ int main (void)
|| crypto_box_SECRETKEYBYTES != 32
# error "libsodium not built correctly"
# endif
puts ("This tool generates a keypair for the libzmq CURVE security mechanism,");
puts ("and encodes the keypair to give two printable strings that you can use");
puts ("in configuration files or source code. The encoding uses Z85, which is");
puts ("a base-85 format that is described in 0MQ RFC 32, and which has an");
puts ("implementation in the Z85.c source used by this tool. The keypair");
puts ("always works with the secret key held by one party and the public key");
puts ("distributed (securely!) to peers wishing to connect to it. CURVE is");
puts ("defined by http://rfc.zeromq.org/spec:25. Z85 is defined by");
puts ("http://rfc.zeromq.org/spec:32.");
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");
char encoded [40];
Z85_encode (encoded, public_key, 32);
puts ("\n== CURVE PUBLIC KEY ==");
puts (encoded);
Z85_encode (encoded, secret_key, 32);
puts ("\n== CURVE SECRET KEY ==");
puts (encoded);
exit (0);
}

108
tools/z85_codec.h Normal file
View File

@ -0,0 +1,108 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
// Maps base 256 to base 85
static char encoder [85 + 1] = {
"0123456789" "abcdefghij" "klmnopqrst" "uvwxyzABCD"
"EFGHIJKLMN" "OPQRSTUVWX" "YZ.-:+=^!/" "*?&<>()[]{"
"}@%$#"
};
// Maps base 85 to base 256
// We chop off lower 32 and higher 128 ranges
static uint8_t decoder [96] = {
0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
};
// --------------------------------------------------------------------------
// Encode a binary frame as a string; destination string MUST be at least
// size * 5 / 4 bytes long. Returns dest. Size must be a multiple of 4.
char *
Z85_encode (char *dest, uint8_t *data, size_t size)
{
assert (size % 4 == 0);
uint char_nbr = 0;
uint byte_nbr = 0;
uint32_t value = 0;
while (byte_nbr < size) {
// Accumulate value in base 256 (binary)
value = value * 256 + data [byte_nbr++];
if (byte_nbr % 4 == 0) {
// Output value in base 85
uint divisor = 85 * 85 * 85 * 85;
while (divisor) {
dest [char_nbr++] = encoder [value / divisor % 85];
divisor /= 85;
}
value = 0;
}
}
assert (char_nbr == size * 5 / 4);
dest [char_nbr] = 0;
return dest;
}
// --------------------------------------------------------------------------
// Decode an encoded string into a binary frame; dest must be at least
// strlen (string) * 4 / 5 bytes long. Returns dest. strlen (string)
// must be a multiple of 5.
uint8_t *
Z85_decode (uint8_t *dest, char *string)
{
assert (strlen (string) % 5 == 0);
uint byte_nbr = 0;
uint char_nbr = 0;
uint32_t value = 0;
while (char_nbr < strlen (string)) {
// Accumulate value in base 85
value = value * 85 + decoder [(uint8_t) string [char_nbr++] - 32];
if (char_nbr % 5 == 0) {
// Output value in base 256
uint divisor = 256 * 256 * 256;
while (divisor) {
dest [byte_nbr++] = value / divisor % 256;
divisor /= 256;
}
value = 0;
}
}
assert (byte_nbr == strlen (string) * 4 / 5);
return dest;
}