Browse Source

Merge branch 'bug18280_029_03_nm_squashed'

Nick Mathewson 7 years ago
parent
commit
48b25e6811
3 changed files with 119 additions and 12 deletions
  1. 24 12
      src/common/util_format.c
  2. 1 0
      src/common/util_format.h
  3. 94 0
      src/test/test_util_format.c

+ 24 - 12
src/common/util_format.c

@@ -21,33 +21,46 @@
 #include <string.h>
 #include <stdlib.h>
 
-/** Implements base32 encoding as in RFC 4648.  Limitation: Requires
- * that srclen*8 is a multiple of 5.
- */
+
+/* Return the base32 encoded size in bytes using the source length srclen.
+ * The NUL terminated byte is added as well since every base32 encoding
+ * requires enough space for it. */
+size_t
+base32_encoded_size(size_t srclen)
+{
+  size_t enclen;
+  enclen = CEIL_DIV(srclen*8, 5) + 1;
+  tor_assert(enclen < INT_MAX && enclen > srclen);
+  return enclen;
+}
+
+/** Implements base32 encoding as in RFC 4648. */
 void
 base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
 {
   unsigned int i, v, u;
-  size_t nbits = srclen * 8, bit;
+  size_t nbits = srclen * 8;
+  size_t bit;
 
   tor_assert(srclen < SIZE_T_CEILING/8);
-  tor_assert((nbits%5) == 0); /* We need an even multiple of 5 bits. */
-  tor_assert((nbits/5)+1 <= destlen); /* We need enough space. */
+  /* We need enough space for the encoded data and the extra NUL byte. */
+  tor_assert(base32_encoded_size(srclen) <= destlen);
   tor_assert(destlen < SIZE_T_CEILING);
 
   for (i=0,bit=0; bit < nbits; ++i, bit+=5) {
     /* set v to the 16-bit value starting at src[bits/8], 0-padded. */
     v = ((uint8_t)src[bit/8]) << 8;
-    if (bit+5<nbits) v += (uint8_t)src[(bit/8)+1];
-    /* set u to the 5-bit value at the bit'th bit of src. */
+    if (bit+5<nbits)
+      v += (uint8_t)src[(bit/8)+1];
+    /* set u to the 5-bit value at the bit'th bit of buf. */
     u = (v >> (11-(bit%8))) & 0x1F;
     dest[i] = BASE32_CHARS[u];
   }
   dest[i] = '\0';
 }
 
-/** Implements base32 decoding as in RFC 4648.  Limitation: Requires
- * that srclen*5 is a multiple of 8. Returns 0 if successful, -1 otherwise.
+/** Implements base32 decoding as in RFC 4648.
+ * Returns 0 if successful, -1 otherwise.
  */
 int
 base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
@@ -57,10 +70,9 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
   unsigned int i;
   size_t nbits, j, bit;
   char *tmp;
-  nbits = srclen * 5;
+  nbits = ((srclen * 5) / 8) * 8;
 
   tor_assert(srclen < SIZE_T_CEILING / 5);
-  tor_assert((nbits%8) == 0); /* We need an even multiple of 8 bits. */
   tor_assert((nbits/8) <= destlen); /* We need enough space. */
   tor_assert(destlen < SIZE_T_CEILING);
 

+ 1 - 0
src/common/util_format.h

@@ -24,6 +24,7 @@ int base64_decode_nopad(uint8_t *dest, size_t destlen,
 #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
 void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
 int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+size_t base32_encoded_size(size_t srclen);
 
 int hex_decode_digit(char c);
 void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);

+ 94 - 0
src/test/test_util_format.c

@@ -289,6 +289,96 @@ test_util_format_base16_decode(void *ignored)
   tor_free(real_dst);
 }
 
+static void
+test_util_format_base32_encode(void *arg)
+{
+  (void) arg;
+  size_t real_dstlen = 32;
+  char *dst = tor_malloc_zero(real_dstlen);
+
+  /* Basic use case that doesn't require a source length correction. */
+  {
+    /* Length of 10 bytes. */
+    const char *src = "blahbleh12";
+    size_t srclen = strlen(src);
+    /* Expected result encoded base32. This was created using python as
+     * such (and same goes for all test case.):
+     *
+     *  b = bytes("blahbleh12", 'utf-8')
+     *  base64.b32encode(b)
+     *  (result in lower case)
+     */
+    const char *expected = "mjwgc2dcnrswqmjs";
+
+    base32_encode(dst, base32_encoded_size(srclen), src, srclen);
+    tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+    /* Encode but to a larger size destination. */
+    memset(dst, 0, real_dstlen);
+    base32_encode(dst, real_dstlen, src, srclen);
+    tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+  }
+
+
+  /* Non multiple of 5 for the source buffer length. */
+  {
+    /* Length of 8 bytes. */
+    const char *expected = "mjwgc2dcnrswq";
+    const char *src = "blahbleh";
+    size_t srclen = strlen(src);
+
+    memset(dst, 0, real_dstlen);
+    base32_encode(dst, base32_encoded_size(srclen), src, srclen);
+    tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+  }
+
+ done:
+  tor_free(dst);
+}
+
+static void
+test_util_format_base32_decode(void *arg)
+{
+  (void) arg;
+  int ret;
+  size_t real_dstlen = 32;
+  char *dst = tor_malloc_zero(real_dstlen);
+
+  /* Basic use case. */
+  {
+    /* Length of 10 bytes. */
+    const char *expected = "blahbleh12";
+    /* Expected result encoded base32. */
+    const char *src = "mjwgc2dcnrswqmjs";
+
+    ret = base32_decode(dst, strlen(expected), src, strlen(src));
+    tt_int_op(ret, ==, 0);
+    tt_str_op(expected, OP_EQ, dst);
+  }
+
+  /* Non multiple of 5 for the source buffer length. */
+  {
+    /* Length of 8 bytes. */
+    const char *expected = "blahbleh";
+    const char *src = "mjwgc2dcnrswq";
+
+    ret = base32_decode(dst, strlen(expected), src, strlen(src));
+    tt_int_op(ret, ==, 0);
+    tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+  }
+
+  /* Invalid values. */
+  {
+    /* Invalid character '#'. */
+    ret = base32_decode(dst, real_dstlen, "#abcde", 6);
+    tt_int_op(ret, ==, -1);
+    /* Make sure the destination buffer has been zeroed even on error. */
+    tt_int_op(tor_mem_is_zero(dst, real_dstlen), ==, 1);
+  }
+
+done:
+  tor_free(dst);
+}
+
 struct testcase_t util_format_tests[] = {
   { "unaligned_accessors", test_util_format_unaligned_accessors, 0,
     NULL, NULL },
@@ -297,6 +387,10 @@ struct testcase_t util_format_tests[] = {
     NULL, NULL },
   { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL },
   { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL },
+  { "base32_encode", test_util_format_base32_encode, 0,
+    NULL, NULL },
+  { "base32_decode", test_util_format_base32_decode, 0,
+    NULL, NULL },
   END_OF_TESTCASES
 };