diff --git a/Makefile.am b/Makefile.am index 962a53eb..4c20aa25 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/configure.ac b/configure.ac index 03576e47..20008a6c 100644 --- a/configure.ac +++ b/configure.ac @@ -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 - diff --git a/doc/zmq_curve.txt b/doc/zmq_curve.txt index a414dfa4..a3ac30da 100644 --- a/doc/zmq_curve.txt +++ b/doc/zmq_curve.txt @@ -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}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7 + +secret: + 8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7 + JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6 ---- SEE ALSO diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index 9d67c818..a467512e 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -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 diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 0f391652..ed3cf25c 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 19e63478..62a8ba08 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,6 +89,7 @@ libzmq_la_SOURCES = \ xsub.hpp \ ypipe.hpp \ yqueue.hpp \ + z85_codec.hpp \ address.cpp \ clock.cpp \ ctx.cpp \ diff --git a/src/options.cpp b/src/options.cpp index c717c253..802ee6b4 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -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 } diff --git a/src/options.hpp b/src/options.hpp index 1b706b71..e1e09ab0 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -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 { diff --git a/src/z85_codec.hpp b/src/z85_codec.hpp new file mode 100644 index 00000000..d8a0e996 --- /dev/null +++ b/src/z85_codec.hpp @@ -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 . +*/ + +// 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 \ No newline at end of file diff --git a/tests/test_security_curve.cpp b/tests/test_security_curve.cpp index a5c921a9..59e9fca6 100644 --- a/tests/test_security_curve.cpp +++ b/tests/test_security_curve.cpp @@ -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 #include #include +#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); } diff --git a/tools/z85_codec.h b/tools/z85_codec.h new file mode 100644 index 00000000..523509d6 --- /dev/null +++ b/tools/z85_codec.h @@ -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 . +*/ + +// Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding + +#include +#include +#include +#include + +// 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; +}