From 6a9425ddc6fa5de32bb97f05b46ab47c01106f80 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 4 Mar 2012 13:51:40 +0000 Subject: [PATCH] LRN: adding generic functions for conversion of binary data to ascii and back: GNUNET_CRYPTO_string_to_data and GNUNET_CRYPTO_data_to_string --- src/include/gnunet_crypto_lib.h | 33 +++++++++ src/util/crypto_hash.c | 119 +++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index 3d2c946b0..640df8b0b 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -916,6 +916,39 @@ GNUNET_CRYPTO_rsa_verify (uint32_t purpose, void GNUNET_CRYPTO_random_disable_entropy_gathering (void); +/** + * Convert binary data to ASCII encoding. The ASCII encoding is rather + * GNUnet specific. It was chosen such that it only uses characters + * in [0-9A-V], can be produced without complex arithmetics and uses a + * small number of characters. The GNUnet encoding uses 103 characters. + * Does not append 0-terminator, but returns a pointer to the place where + * it should be placed, if needed. + * + * @param data data to encode + * @param size size of data (in bytes) + * @param out buffer to fill + * @param out_size size of the buffer. Must be large enough to hold + * ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 + * @return pointer to the next byte in 'out' or NULL on error. + */ +char * +GNUNET_CRYPTO_data_to_string (unsigned char *data, size_t size, + char *out, size_t out_size); + +/** + * Convert ASCII encoding back to data + * out_size must match exactly the size of the data before it was encoded. + * + * @param enc the encoding + * @param enclen number of characters in 'enc' (without 0-terminator, which can be missing) + * @param out location where to store the decoded data + * @param out_size sizeof the output buffer + * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding + */ +int +GNUNET_CRYPTO_string_to_data (const char *enc, size_t enclen, + unsigned char *out, size_t out_size); + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/util/crypto_hash.c b/src/util/crypto_hash.c index 63d965406..dce449ff0 100644 --- a/src/util/crypto_hash.c +++ b/src/util/crypto_hash.c @@ -241,7 +241,6 @@ GNUNET_CRYPTO_hash_file_cancel (struct GNUNET_CRYPTO_FileHashContext *fhc) } - /* ***************** binary-ASCII encoding *************** */ /** @@ -261,6 +260,124 @@ getValue__ (unsigned char a) } +/** + * Convert binary data to ASCII encoding. The ASCII encoding is rather + * GNUnet specific. It was chosen such that it only uses characters + * in [0-9A-V], can be produced without complex arithmetics and uses a + * small number of characters. The GNUnet encoding uses 103 characters. + * Does not append 0-terminator, but returns a pointer to the place where + * it should be placed, if needed. + * + * @param data data to encode + * @param size size of data (in bytes) + * @param out buffer to fill + * @param out_size size of the buffer. Must be large enough to hold + * ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes + * @return pointer to the next byte in 'out' or NULL on error. + */ +char * +GNUNET_CRYPTO_data_to_string (unsigned char *data, size_t size, char *out, size_t out_size) +{ + /** + * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters) + */ + static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + unsigned int wpos; + unsigned int rpos; + unsigned int bits; + unsigned int vbit; + + GNUNET_assert (data != NULL); + GNUNET_assert (out != NULL); + GNUNET_assert (out_size >= (((size*8) + ((size*8) % 5)) % 5)); + vbit = 0; + wpos = 0; + rpos = 0; + bits = 0; + while ((rpos < size) || (vbit > 0)) + { + if ((rpos < size) && (vbit < 5)) + { + bits = (bits << 8) | data[rpos++]; /* eat 8 more bits */ + vbit += 8; + } + if (vbit < 5) + { + bits <<= (5 - vbit); /* zero-padding */ + GNUNET_assert (vbit == ((size * 8) % 5)); + vbit = 5; + } + if (wpos >= out_size) + return NULL; + out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; + vbit -= 5; + } + if (wpos != out_size) + return NULL; + GNUNET_assert (vbit == 0); + return &out[wpos]; +} + + +/** + * Convert ASCII encoding back to data + * out_size must match exactly the size of the data before it was encoded. + * + * @param enc the encoding + * @param enclen number of characters in 'enc' (without 0-terminator, which can be missing) + * @param out location where to store the decoded data + * @param out_size sizeof the output buffer + * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding + */ +int +GNUNET_CRYPTO_string_to_data (const char *enc, size_t enclen, + unsigned char *out, size_t out_size) +{ + unsigned int rpos; + unsigned int wpos; + unsigned int bits; + unsigned int vbit; + int ret; + int shift; + int encoded_len = out_size * 8; + if (encoded_len % 5 > 0) + { + vbit = encoded_len % 5; /* padding! */ + shift = 5 - vbit; + } + else + { + vbit = 0; + shift = 0; + } + if ((encoded_len + shift) / 5 != enclen) + return GNUNET_SYSERR; + + wpos = out_size; + rpos = enclen; + bits = (ret = getValue__ (enc[--rpos])) >> (5 - encoded_len % 5); + if (-1 == ret) + return GNUNET_SYSERR; + while (wpos > 0) + { + GNUNET_assert (rpos > 0); + bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits; + if (-1 == ret) + return GNUNET_SYSERR; + vbit += 5; + if (vbit >= 8) + { + out[--wpos] = (unsigned char) bits; + bits >>= 8; + vbit -= 8; + } + } + GNUNET_assert (rpos == 0); + GNUNET_assert (vbit == 0); + return GNUNET_OK; +} + + /** * Convert GNUNET_CRYPTO_hash to ASCII encoding. The ASCII encoding is rather * GNUnet specific. It was chosen such that it only uses characters -- 2.25.1