Browse Source

Move formatting functions around.

The base64 and base32 functions used to be in crypto.c;
crypto_format.h had no header; some general-purpose functions were in
crypto_curve25519.c.

This patch makes a {crypto,util}_format.[ch], and puts more functions
there.  Small modules are beautiful!
Nick Mathewson 8 years ago
parent
commit
347fe449fe

+ 1 - 0
src/common/address.c

@@ -37,6 +37,7 @@
 
 #include "compat.h"
 #include "util.h"
+#include "util_format.h"
 #include "address.h"
 #include "torlog.h"
 #include "container.h"

+ 2 - 481
src/common/crypto.c

@@ -27,6 +27,7 @@
 #include "crypto.h"
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
+#include "crypto_format.h"
 
 #if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
 #error "We require OpenSSL >= 1.0.0"
@@ -62,6 +63,7 @@
 #include "container.h"
 #include "compat.h"
 #include "sandbox.h"
+#include "util_format.h"
 
 #ifdef ANDROID
 /* Android's OpenSSL seems to have removed all of its Engine support. */
@@ -2541,487 +2543,6 @@ smartlist_shuffle(smartlist_t *sl)
   }
 }
 
-#define BASE64_OPENSSL_LINELEN 64
-
-/** Return the Base64 encoded size of <b>srclen</b> bytes of data in
- * bytes.
- *
- * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return the size
- * of the encoded output as multiline output (64 character, `\n' terminated
- * lines).
- */
-size_t
-base64_encode_size(size_t srclen, int flags)
-{
-  size_t enclen;
-  tor_assert(srclen < INT_MAX);
-
-  if (srclen == 0)
-    return 0;
-
-  enclen = ((srclen - 1) / 3) * 4 + 4;
-  if (flags & BASE64_ENCODE_MULTILINE) {
-    size_t remainder = enclen % BASE64_OPENSSL_LINELEN;
-    enclen += enclen / BASE64_OPENSSL_LINELEN;
-    if (remainder)
-      enclen++;
-  }
-  tor_assert(enclen < INT_MAX && enclen > srclen);
-  return enclen;
-}
-
-/** Internal table mapping 6 bit values to the Base64 alphabet. */
-static const char base64_encode_table[64] = {
-  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
-  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
-  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
-  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
-  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
-  'w', 'x', 'y', 'z', '0', '1', '2', '3',
-  '4', '5', '6', '7', '8', '9', '+', '/'
-};
-
-/** Base64 encode <b>srclen</b> bytes of data from <b>src</b>.  Write
- * the result into <b>dest</b>, if it will fit within <b>destlen</b>
- * bytes. Return the number of bytes written on success; -1 if
- * destlen is too short, or other failure.
- *
- * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return encoded
- * output in multiline format (64 character, `\n' terminated lines).
- */
-int
-base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
-              int flags)
-{
-  const unsigned char *usrc = (unsigned char *)src;
-  const unsigned char *eous = usrc + srclen;
-  char *d = dest;
-  uint32_t n = 0;
-  size_t linelen = 0;
-  size_t enclen;
-  int n_idx = 0;
-
-  if (!src || !dest)
-    return -1;
-
-  /* Ensure that there is sufficient space, including the NUL. */
-  enclen = base64_encode_size(srclen, flags);
-  if (destlen < enclen + 1)
-    return -1;
-  if (destlen > SIZE_T_CEILING)
-    return -1;
-  if (enclen > INT_MAX)
-    return -1;
-
-  memset(dest, 0, enclen);
-
-  /* XXX/Yawning: If this ends up being too slow, this can be sped up
-   * by separating the multiline format case and the normal case, and
-   * processing 48 bytes of input at a time when newlines are desired.
-   */
-#define ENCODE_CHAR(ch) \
-  STMT_BEGIN                                                    \
-    *d++ = ch;                                                  \
-    if (flags & BASE64_ENCODE_MULTILINE) {                      \
-      if (++linelen % BASE64_OPENSSL_LINELEN == 0) {            \
-        linelen = 0;                                            \
-        *d++ = '\n';                                            \
-      }                                                         \
-    }                                                           \
-  STMT_END
-
-#define ENCODE_N(idx) \
-  ENCODE_CHAR(base64_encode_table[(n >> ((3 - idx) * 6)) & 0x3f])
-
-#define ENCODE_PAD() ENCODE_CHAR('=')
-
-  /* Iterate over all the bytes in src.  Each one will add 8 bits to the
-   * value we're encoding.  Accumulate bits in <b>n</b>, and whenever we
-   * have 24 bits, batch them into 4 bytes and flush those bytes to dest.
-   */
-  for ( ; usrc < eous; ++usrc) {
-    n = (n << 8) | *usrc;
-    if ((++n_idx) == 3) {
-      ENCODE_N(0);
-      ENCODE_N(1);
-      ENCODE_N(2);
-      ENCODE_N(3);
-      n_idx = 0;
-      n = 0;
-    }
-  }
-  switch (n_idx) {
-  case 0:
-    /* 0 leftover bits, no pading to add. */
-    break;
-  case 1:
-    /* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed
-     * by 2 padding characters.
-     */
-    n <<= 4;
-    ENCODE_N(2);
-    ENCODE_N(3);
-    ENCODE_PAD();
-    ENCODE_PAD();
-    break;
-  case 2:
-    /* 16 leftover bits, pad to 18 bits, write the 3 6-bit values followed
-     * by 1 padding character.
-     */
-    n <<= 2;
-    ENCODE_N(1);
-    ENCODE_N(2);
-    ENCODE_N(3);
-    ENCODE_PAD();
-    break;
-  default:
-    /* Something went catastrophically wrong. */
-    tor_fragile_assert();
-    return -1;
-  }
-
-#undef ENCODE_N
-#undef ENCODE_PAD
-#undef ENCODE_CHAR
-
-  /* Multiline output always includes at least one newline. */
-  if (flags & BASE64_ENCODE_MULTILINE && linelen != 0)
-    *d++ = '\n';
-
-  tor_assert(d - dest == (ptrdiff_t)enclen);
-
-  *d++ = '\0'; /* NUL terminate the output. */
-
-  return (int) enclen;
-}
-
-/** As base64_encode, but do not add any internal spaces or external padding
- * to the output stream. */
-int
-base64_encode_nopad(char *dest, size_t destlen,
-                    const uint8_t *src, size_t srclen)
-{
-  int n = base64_encode(dest, destlen, (const char*) src, srclen, 0);
-  if (n <= 0)
-    return n;
-  tor_assert((size_t)n < destlen && dest[n] == 0);
-  char *in, *out;
-  in = out = dest;
-  while (*in) {
-    if (*in == '=' || *in == '\n') {
-      ++in;
-    } else {
-      *out++ = *in++;
-    }
-  }
-  *out = 0;
-
-  tor_assert(out - dest <= INT_MAX);
-
-  return (int)(out - dest);
-}
-
-/** As base64_decode, but do not require any padding on the input */
-int
-base64_decode_nopad(uint8_t *dest, size_t destlen,
-                    const char *src, size_t srclen)
-{
-  if (srclen > SIZE_T_CEILING - 4)
-    return -1;
-  char *buf = tor_malloc(srclen + 4);
-  memcpy(buf, src, srclen+1);
-  size_t buflen;
-  switch (srclen % 4)
-    {
-    case 0:
-    default:
-      buflen = srclen;
-      break;
-    case 1:
-      tor_free(buf);
-      return -1;
-    case 2:
-      memcpy(buf+srclen, "==", 3);
-      buflen = srclen + 2;
-      break;
-    case 3:
-      memcpy(buf+srclen, "=", 2);
-      buflen = srclen + 1;
-      break;
-  }
-  int n = base64_decode((char*)dest, destlen, buf, buflen);
-  tor_free(buf);
-  return n;
-}
-
-#undef BASE64_OPENSSL_LINELEN
-
-/** @{ */
-/** Special values used for the base64_decode_table */
-#define X 255
-#define SP 64
-#define PAD 65
-/** @} */
-/** Internal table mapping byte values to what they represent in base64.
- * Numbers 0..63 are 6-bit integers.  SPs are spaces, and should be
- * skipped.  Xs are invalid and must not appear in base64. PAD indicates
- * end-of-string. */
-static const uint8_t base64_decode_table[256] = {
-  X, X, X, X, X, X, X, X, X, SP, SP, SP, X, SP, X, X, /* */
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  SP, X, X, X, X, X, X, X, X, X, X, 62, X, X, X, 63,
-  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, X, X, X, PAD, X, X,
-  X, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
-  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, X, X, X, X, X,
-  X, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
-};
-
-/** Base64 decode <b>srclen</b> bytes of data from <b>src</b>.  Write
- * the result into <b>dest</b>, if it will fit within <b>destlen</b>
- * bytes.  Return the number of bytes written on success; -1 if
- * destlen is too short, or other failure.
- *
- * NOTE 1: destlen is checked conservatively, as though srclen contained no
- * spaces or padding.
- *
- * NOTE 2: This implementation does not check for the correct number of
- * padding "=" characters at the end of the string, and does not check
- * for internal padding characters.
- */
-int
-base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
-{
-  const char *eos = src+srclen;
-  uint32_t n=0;
-  int n_idx=0;
-  char *dest_orig = dest;
-
-  /* Max number of bits == srclen*6.
-   * Number of bytes required to hold all bits == (srclen*6)/8.
-   * Yes, we want to round down: anything that hangs over the end of a
-   * byte is padding. */
-  if (destlen < (srclen*3)/4)
-    return -1;
-  if (destlen > SIZE_T_CEILING)
-    return -1;
-
-  memset(dest, 0, destlen);
-
-  /* Iterate over all the bytes in src.  Each one will add 0 or 6 bits to the
-   * value we're decoding.  Accumulate bits in <b>n</b>, and whenever we have
-   * 24 bits, batch them into 3 bytes and flush those bytes to dest.
-   */
-  for ( ; src < eos; ++src) {
-    unsigned char c = (unsigned char) *src;
-    uint8_t v = base64_decode_table[c];
-    switch (v) {
-      case X:
-        /* This character isn't allowed in base64. */
-        return -1;
-      case SP:
-        /* This character is whitespace, and has no effect. */
-        continue;
-      case PAD:
-        /* We've hit an = character: the data is over. */
-        goto end_of_loop;
-      default:
-        /* We have an actual 6-bit value.  Append it to the bits in n. */
-        n = (n<<6) | v;
-        if ((++n_idx) == 4) {
-          /* We've accumulated 24 bits in n. Flush them. */
-          *dest++ = (n>>16);
-          *dest++ = (n>>8) & 0xff;
-          *dest++ = (n) & 0xff;
-          n_idx = 0;
-          n = 0;
-        }
-    }
-  }
- end_of_loop:
-  /* If we have leftover bits, we need to cope. */
-  switch (n_idx) {
-    case 0:
-    default:
-      /* No leftover bits.  We win. */
-      break;
-    case 1:
-      /* 6 leftover bits. That's invalid; we can't form a byte out of that. */
-      return -1;
-    case 2:
-      /* 12 leftover bits: The last 4 are padding and the first 8 are data. */
-      *dest++ = n >> 4;
-      break;
-    case 3:
-      /* 18 leftover bits: The last 2 are padding and the first 16 are data. */
-      *dest++ = n >> 10;
-      *dest++ = n >> 2;
-  }
-
-  tor_assert((dest-dest_orig) <= (ssize_t)destlen);
-  tor_assert((dest-dest_orig) <= INT_MAX);
-
-  return (int)(dest-dest_orig);
-}
-#undef X
-#undef SP
-#undef PAD
-
-/** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing =
- * characters, and store the nul-terminated result in the first
- * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>.  */
-/* XXXX unify with crypto_format.c code */
-int
-digest_to_base64(char *d64, const char *digest)
-{
-  char buf[256];
-  base64_encode(buf, sizeof(buf), digest, DIGEST_LEN, 0);
-  buf[BASE64_DIGEST_LEN] = '\0';
-  memcpy(d64, buf, BASE64_DIGEST_LEN+1);
-  return 0;
-}
-
-/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
- * trailing newline or = characters), decode it and store the result in the
- * first DIGEST_LEN bytes at <b>digest</b>. */
-/* XXXX unify with crypto_format.c code */
-int
-digest_from_base64(char *digest, const char *d64)
-{
-  if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN)
-    return 0;
-  else
-    return -1;
-}
-
-/** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
- * trailing = characters, and store the nul-terminated result in the first
- * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
- /* XXXX unify with crypto_format.c code */
-int
-digest256_to_base64(char *d64, const char *digest)
-{
-  char buf[256];
-  base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN, 0);
-  buf[BASE64_DIGEST256_LEN] = '\0';
-  memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
-  return 0;
-}
-
-/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
- * trailing newline or = characters), decode it and store the result in the
- * first DIGEST256_LEN bytes at <b>digest</b>. */
-/* XXXX unify with crypto_format.c code */
-int
-digest256_from_base64(char *digest, const char *d64)
-{
-  if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN)
-    return 0;
-  else
-    return -1;
-}
-
-/** Implements base32 encoding as in RFC 4648.  Limitation: Requires
- * that srclen*8 is a multiple of 5.
- */
-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;
-
-  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. */
-  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. */
-    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.
- */
-int
-base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
-{
-  /* XXXX we might want to rewrite this along the lines of base64_decode, if
-   * it ever shows up in the profile. */
-  unsigned int i;
-  size_t nbits, j, bit;
-  char *tmp;
-  nbits = srclen * 5;
-
-  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);
-
-  memset(dest, 0, destlen);
-
-  /* Convert base32 encoded chars to the 5-bit values that they represent. */
-  tmp = tor_malloc_zero(srclen);
-  for (j = 0; j < srclen; ++j) {
-    if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61;
-    else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18;
-    else if (src[j] > 0x40 && src[j] < 0x5B) tmp[j] = src[j] - 0x41;
-    else {
-      log_warn(LD_BUG, "illegal character in base32 encoded string");
-      tor_free(tmp);
-      return -1;
-    }
-  }
-
-  /* Assemble result byte-wise by applying five possible cases. */
-  for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) {
-    switch (bit % 40) {
-    case 0:
-      dest[i] = (((uint8_t)tmp[(bit/5)]) << 3) +
-                (((uint8_t)tmp[(bit/5)+1]) >> 2);
-      break;
-    case 8:
-      dest[i] = (((uint8_t)tmp[(bit/5)]) << 6) +
-                (((uint8_t)tmp[(bit/5)+1]) << 1) +
-                (((uint8_t)tmp[(bit/5)+2]) >> 4);
-      break;
-    case 16:
-      dest[i] = (((uint8_t)tmp[(bit/5)]) << 4) +
-                (((uint8_t)tmp[(bit/5)+1]) >> 1);
-      break;
-    case 24:
-      dest[i] = (((uint8_t)tmp[(bit/5)]) << 7) +
-                (((uint8_t)tmp[(bit/5)+1]) << 2) +
-                (((uint8_t)tmp[(bit/5)+2]) >> 3);
-      break;
-    case 32:
-      dest[i] = (((uint8_t)tmp[(bit/5)]) << 5) +
-                ((uint8_t)tmp[(bit/5)+1]);
-      break;
-    }
-  }
-
-  memwipe(tmp, 0, srclen);
-  tor_free(tmp);
-  tmp = NULL;
-  return 0;
-}
-
 /**
  * Destroy the <b>sz</b> bytes of data stored at <b>mem</b>, setting them to
  * the value <b>byte</b>.

+ 0 - 20
src/common/crypto.h

@@ -278,26 +278,6 @@ struct smartlist_t;
 void *smartlist_choose(const struct smartlist_t *sl);
 void smartlist_shuffle(struct smartlist_t *sl);
 
-#define BASE64_ENCODE_MULTILINE 1
-size_t base64_encode_size(size_t srclen, int flags);
-int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
-                  int flags);
-int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
-int base64_encode_nopad(char *dest, size_t destlen,
-                        const uint8_t *src, size_t srclen);
-int base64_decode_nopad(uint8_t *dest, size_t destlen,
-                        const char *src, size_t srclen);
-
-/** Characters that can appear (case-insensitively) in a base32 encoding. */
-#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);
-
-int digest_to_base64(char *d64, const char *digest);
-int digest_from_base64(char *digest, const char *d64);
-int digest256_to_base64(char *d64, const char *digest);
-int digest256_from_base64(char *digest, const char *d64);
-
 /** OpenSSL-based utility functions. */
 void memwipe(void *mem, uint8_t byte, size_t sz);
 

+ 1 - 100
src/common/crypto_curve25519.c

@@ -11,6 +11,7 @@
 #include "container.h"
 #include "crypto.h"
 #include "crypto_curve25519.h"
+#include "crypto_format.h"
 #include "util.h"
 #include "torlog.h"
 
@@ -160,106 +161,6 @@ curve25519_keypair_generate(curve25519_keypair_t *keypair_out,
   return 0;
 }
 
-/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
- * <b>fname</b> in the tagged-data format.  This format contains a
- * 32-byte header, followed by the data itself.  The header is the
- * NUL-padded string "== <b>typestring</b>: <b>tag</b> ==".  The length
- * of <b>typestring</b> and <b>tag</b> must therefore be no more than
- * 24.
- **/
-int
-crypto_write_tagged_contents_to_file(const char *fname,
-                                     const char *typestring,
-                                     const char *tag,
-                                     const uint8_t *data,
-                                     size_t datalen)
-{
-  char header[32];
-  smartlist_t *chunks = smartlist_new();
-  sized_chunk_t ch0, ch1;
-  int r = -1;
-
-  memset(header, 0, sizeof(header));
-  if (tor_snprintf(header, sizeof(header),
-                   "== %s: %s ==", typestring, tag) < 0)
-    goto end;
-  ch0.bytes = header;
-  ch0.len = 32;
-  ch1.bytes = (const char*) data;
-  ch1.len = datalen;
-  smartlist_add(chunks, &ch0);
-  smartlist_add(chunks, &ch1);
-
-  r = write_chunks_to_file(fname, chunks, 1, 0);
-
- end:
-  smartlist_free(chunks);
-  return r;
-}
-
-/** Read a tagged-data file from <b>fname</b> into the
- * <b>data_out_len</b>-byte buffer in <b>data_out</b>. Check that the
- * typestring matches <b>typestring</b>; store the tag into a newly allocated
- * string in <b>tag_out</b>. Return -1 on failure, and the number of bytes of
- * data on success.  Preserves the errno from reading the file. */
-ssize_t
-crypto_read_tagged_contents_from_file(const char *fname,
-                                      const char *typestring,
-                                      char **tag_out,
-                                      uint8_t *data_out,
-                                      ssize_t data_out_len)
-{
-  char prefix[33];
-  char *content = NULL;
-  struct stat st;
-  ssize_t r = -1;
-  size_t st_size = 0;
-  int saved_errno = 0;
-
-  *tag_out = NULL;
-  st.st_size = 0;
-  content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
-  if (! content) {
-    saved_errno = errno;
-    goto end;
-  }
-  if (st.st_size < 32 || st.st_size > 32 + data_out_len) {
-    saved_errno = EINVAL;
-    goto end;
-  }
-  st_size = (size_t)st.st_size;
-
-  memcpy(prefix, content, 32);
-  prefix[32] = 0;
-  /* Check type, extract tag. */
-  if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") ||
-      ! tor_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) {
-    saved_errno = EINVAL;
-    goto end;
-  }
-
-  if (strcmpstart(prefix+3, typestring) ||
-      3+strlen(typestring) >= 32 ||
-      strcmpstart(prefix+3+strlen(typestring), ": ")) {
-    saved_errno = EINVAL;
-    goto end;
-  }
-
-  *tag_out = tor_strndup(prefix+5+strlen(typestring),
-                         strlen(prefix)-8-strlen(typestring));
-
-  memcpy(data_out, content+32, st_size-32);
-  r = st_size - 32;
-
- end:
-  if (content)
-    memwipe(content, 0, st_size);
-  tor_free(content);
-  if (saved_errno)
-    errno = saved_errno;
-  return r;
-}
-
 /** DOCDOC */
 int
 curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair,

+ 0 - 12
src/common/crypto_curve25519.h

@@ -72,18 +72,6 @@ int curve25519_public_from_base64(curve25519_public_key_t *pkey,
 int curve25519_public_to_base64(char *output,
                                 const curve25519_public_key_t *pkey);
 
-int crypto_write_tagged_contents_to_file(const char *fname,
-                                         const char *typestring,
-                                         const char *tag,
-                                         const uint8_t *data,
-                                         size_t datalen);
-
-ssize_t crypto_read_tagged_contents_from_file(const char *fname,
-                                              const char *typestring,
-                                              char **tag_out,
-                                              uint8_t *data_out,
-                                              ssize_t data_out_len);
-
 void curve25519_set_impl_params(int use_ed);
 void curve25519_init(void);
 

+ 1 - 0
src/common/crypto_ed25519.c

@@ -12,6 +12,7 @@
 
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
+#include "crypto_format.h"
 #include "torlog.h"
 #include "util.h"
 

+ 0 - 15
src/common/crypto_ed25519.h

@@ -88,21 +88,6 @@ int ed25519_public_blind(ed25519_public_key_t *out,
                          const ed25519_public_key_t *inp,
                          const uint8_t *param);
 
-/* XXXX move these to crypto_format.h */
-#define ED25519_BASE64_LEN 43
-int ed25519_public_from_base64(ed25519_public_key_t *pkey,
-                               const char *input);
-int ed25519_public_to_base64(char *output,
-                             const ed25519_public_key_t *pkey);
-
-/* XXXX move these to crypto_format.h */
-#define ED25519_SIG_BASE64_LEN 86
-
-int ed25519_signature_from_base64(ed25519_signature_t *sig,
-                                  const char *input);
-int ed25519_signature_to_base64(char *output,
-                                const ed25519_signature_t *sig);
-
 /* XXXX read encrypted, write encrypted. */
 
 int ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,

+ 161 - 1
src/common/crypto_format.c

@@ -1,4 +1,7 @@
-/* Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
 /* See LICENSE for licensing information */
 
 /* Formatting and parsing code for crypto-related data structures. */
@@ -7,12 +10,115 @@
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
+#include "container.h"
 #include "crypto.h"
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
+#include "crypto_format.h"
 #include "util.h"
+#include "util_format.h"
 #include "torlog.h"
 
+/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
+ * <b>fname</b> in the tagged-data format.  This format contains a
+ * 32-byte header, followed by the data itself.  The header is the
+ * NUL-padded string "== <b>typestring</b>: <b>tag</b> ==".  The length
+ * of <b>typestring</b> and <b>tag</b> must therefore be no more than
+ * 24.
+ **/
+int
+crypto_write_tagged_contents_to_file(const char *fname,
+                                     const char *typestring,
+                                     const char *tag,
+                                     const uint8_t *data,
+                                     size_t datalen)
+{
+  char header[32];
+  smartlist_t *chunks = smartlist_new();
+  sized_chunk_t ch0, ch1;
+  int r = -1;
+
+  memset(header, 0, sizeof(header));
+  if (tor_snprintf(header, sizeof(header),
+                   "== %s: %s ==", typestring, tag) < 0)
+    goto end;
+  ch0.bytes = header;
+  ch0.len = 32;
+  ch1.bytes = (const char*) data;
+  ch1.len = datalen;
+  smartlist_add(chunks, &ch0);
+  smartlist_add(chunks, &ch1);
+
+  r = write_chunks_to_file(fname, chunks, 1, 0);
+
+ end:
+  smartlist_free(chunks);
+  return r;
+}
+
+/** Read a tagged-data file from <b>fname</b> into the
+ * <b>data_out_len</b>-byte buffer in <b>data_out</b>. Check that the
+ * typestring matches <b>typestring</b>; store the tag into a newly allocated
+ * string in <b>tag_out</b>. Return -1 on failure, and the number of bytes of
+ * data on success.  Preserves the errno from reading the file. */
+ssize_t
+crypto_read_tagged_contents_from_file(const char *fname,
+                                      const char *typestring,
+                                      char **tag_out,
+                                      uint8_t *data_out,
+                                      ssize_t data_out_len)
+{
+  char prefix[33];
+  char *content = NULL;
+  struct stat st;
+  ssize_t r = -1;
+  size_t st_size = 0;
+  int saved_errno = 0;
+
+  *tag_out = NULL;
+  st.st_size = 0;
+  content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
+  if (! content) {
+    saved_errno = errno;
+    goto end;
+  }
+  if (st.st_size < 32 || st.st_size > 32 + data_out_len) {
+    saved_errno = EINVAL;
+    goto end;
+  }
+  st_size = (size_t)st.st_size;
+
+  memcpy(prefix, content, 32);
+  prefix[32] = 0;
+  /* Check type, extract tag. */
+  if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") ||
+      ! tor_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) {
+    saved_errno = EINVAL;
+    goto end;
+  }
+
+  if (strcmpstart(prefix+3, typestring) ||
+      3+strlen(typestring) >= 32 ||
+      strcmpstart(prefix+3+strlen(typestring), ": ")) {
+    saved_errno = EINVAL;
+    goto end;
+  }
+
+  *tag_out = tor_strndup(prefix+5+strlen(typestring),
+                         strlen(prefix)-8-strlen(typestring));
+
+  memcpy(data_out, content+32, st_size-32);
+  r = st_size - 32;
+
+ end:
+  if (content)
+    memwipe(content, 0, st_size);
+  tor_free(content);
+  if (saved_errno)
+    errno = saved_errno;
+  return r;
+}
+
 int
 curve25519_public_to_base64(char *output,
                             const curve25519_public_key_t *pkey)
@@ -104,3 +210,57 @@ ed25519_signature_from_base64(ed25519_signature_t *sig,
   return 0;
 }
 
+/** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing =
+ * characters, and store the nul-terminated result in the first
+ * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>.  */
+/* XXXX unify with crypto_format.c code */
+int
+digest_to_base64(char *d64, const char *digest)
+{
+  char buf[256];
+  base64_encode(buf, sizeof(buf), digest, DIGEST_LEN, 0);
+  buf[BASE64_DIGEST_LEN] = '\0';
+  memcpy(d64, buf, BASE64_DIGEST_LEN+1);
+  return 0;
+}
+
+/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
+ * trailing newline or = characters), decode it and store the result in the
+ * first DIGEST_LEN bytes at <b>digest</b>. */
+/* XXXX unify with crypto_format.c code */
+int
+digest_from_base64(char *digest, const char *d64)
+{
+  if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN)
+    return 0;
+  else
+    return -1;
+}
+
+/** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
+ * trailing = characters, and store the nul-terminated result in the first
+ * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
+ /* XXXX unify with crypto_format.c code */
+int
+digest256_to_base64(char *d64, const char *digest)
+{
+  char buf[256];
+  base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN, 0);
+  buf[BASE64_DIGEST256_LEN] = '\0';
+  memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
+  return 0;
+}
+
+/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
+ * trailing newline or = characters), decode it and store the result in the
+ * first DIGEST256_LEN bytes at <b>digest</b>. */
+/* XXXX unify with crypto_format.c code */
+int
+digest256_from_base64(char *digest, const char *d64)
+{
+  if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN)
+    return 0;
+  else
+    return -1;
+}
+

+ 46 - 0
src/common/crypto_format.h

@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_CRYPTO_FORMAT_H
+#define TOR_CRYPTO_FORMAT_H
+
+#include "testsupport.h"
+#include "torint.h"
+#include "crypto_ed25519.h"
+
+int crypto_write_tagged_contents_to_file(const char *fname,
+                                         const char *typestring,
+                                         const char *tag,
+                                         const uint8_t *data,
+                                         size_t datalen);
+
+ssize_t crypto_read_tagged_contents_from_file(const char *fname,
+                                              const char *typestring,
+                                              char **tag_out,
+                                              uint8_t *data_out,
+                                              ssize_t data_out_len);
+
+#define ED25519_BASE64_LEN 43
+int ed25519_public_from_base64(ed25519_public_key_t *pkey,
+                               const char *input);
+int ed25519_public_to_base64(char *output,
+                             const ed25519_public_key_t *pkey);
+
+/* XXXX move these to crypto_format.h */
+#define ED25519_SIG_BASE64_LEN 86
+
+int ed25519_signature_from_base64(ed25519_signature_t *sig,
+                                  const char *input);
+int ed25519_signature_to_base64(char *output,
+                                const ed25519_signature_t *sig);
+
+int digest_to_base64(char *d64, const char *digest);
+int digest_from_base64(char *digest, const char *d64);
+int digest256_to_base64(char *d64, const char *digest);
+int digest256_from_base64(char *digest, const char *d64);
+
+#endif
+

+ 3 - 0
src/common/include.am

@@ -68,6 +68,7 @@ LIBOR_A_SOURCES = \
   src/common/log.c					\
   src/common/memarea.c					\
   src/common/util.c					\
+  src/common/util_format.c				\
   src/common/util_process.c				\
   src/common/sandbox.c					\
   src/common/workqueue.c				\
@@ -122,6 +123,7 @@ COMMONHEADERS = \
   src/common/crypto.h				\
   src/common/crypto_curve25519.h		\
   src/common/crypto_ed25519.h			\
+  src/common/crypto_format.h			\
   src/common/crypto_pwbox.h			\
   src/common/crypto_s2k.h			\
   src/common/di_ops.h				\
@@ -135,6 +137,7 @@ COMMONHEADERS = \
   src/common/torlog.h				\
   src/common/tortls.h				\
   src/common/util.h				\
+  src/common/util_format.h			\
   src/common/util_process.h			\
   src/common/workqueue.h
 

+ 1 - 85
src/common/util.c

@@ -27,6 +27,7 @@
 #include "sandbox.h"
 #include "backtrace.h"
 #include "util_process.h"
+#include "util_format.h"
 
 #ifdef _WIN32
 #include <io.h>
@@ -1211,91 +1212,6 @@ tor_parse_uint64(const char *s, int base, uint64_t min,
   CHECK_STRTOX_RESULT();
 }
 
-/** Encode the <b>srclen</b> bytes at <b>src</b> in a NUL-terminated,
- * uppercase hexadecimal string; store it in the <b>destlen</b>-byte buffer
- * <b>dest</b>.
- */
-void
-base16_encode(char *dest, size_t destlen, const char *src, size_t srclen)
-{
-  const char *end;
-  char *cp;
-
-  tor_assert(destlen >= srclen*2+1);
-  tor_assert(destlen < SIZE_T_CEILING);
-
-  cp = dest;
-  end = src+srclen;
-  while (src<end) {
-    *cp++ = "0123456789ABCDEF"[ (*(const uint8_t*)src) >> 4 ];
-    *cp++ = "0123456789ABCDEF"[ (*(const uint8_t*)src) & 0xf ];
-    ++src;
-  }
-  *cp = '\0';
-}
-
-/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
-static INLINE int
-hex_decode_digit_(char c)
-{
-  switch (c) {
-    case '0': return 0;
-    case '1': return 1;
-    case '2': return 2;
-    case '3': return 3;
-    case '4': return 4;
-    case '5': return 5;
-    case '6': return 6;
-    case '7': return 7;
-    case '8': return 8;
-    case '9': return 9;
-    case 'A': case 'a': return 10;
-    case 'B': case 'b': return 11;
-    case 'C': case 'c': return 12;
-    case 'D': case 'd': return 13;
-    case 'E': case 'e': return 14;
-    case 'F': case 'f': return 15;
-    default:
-      return -1;
-  }
-}
-
-/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
-int
-hex_decode_digit(char c)
-{
-  return hex_decode_digit_(c);
-}
-
-/** Given a hexadecimal string of <b>srclen</b> bytes in <b>src</b>, decode it
- * and store the result in the <b>destlen</b>-byte buffer at <b>dest</b>.
- * Return 0 on success, -1 on failure. */
-int
-base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
-{
-  const char *end;
-
-  int v1,v2;
-  if ((srclen % 2) != 0)
-    return -1;
-  if (destlen < srclen/2 || destlen > SIZE_T_CEILING)
-    return -1;
-
-  memset(dest, 0, destlen);
-
-  end = src+srclen;
-  while (src<end) {
-    v1 = hex_decode_digit_(*src);
-    v2 = hex_decode_digit_(*(src+1));
-    if (v1<0||v2<0)
-      return -1;
-    *(uint8_t*)dest = (v1<<4)|v2;
-    ++dest;
-    src+=2;
-  }
-  return 0;
-}
-
 /** Allocate and return a new string representing the contents of <b>s</b>,
  * surrounded by quotes and using standard C escapes.
  *

+ 0 - 4
src/common/util.h

@@ -264,10 +264,6 @@ void smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern,
                              va_list args)
   CHECK_PRINTF(2, 0);
 
-int hex_decode_digit(char c);
-void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
-int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
-
 /* Time helpers */
 long tv_udiff(const struct timeval *start, const struct timeval *end);
 long tv_mdiff(const struct timeval *start, const struct timeval *end);

+ 528 - 0
src/common/util_format.c

@@ -0,0 +1,528 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "torlog.h"
+#include "util.h"
+#include "util_format.h"
+#include "torint.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+/** Implements base32 encoding as in RFC 4648.  Limitation: Requires
+ * that srclen*8 is a multiple of 5.
+ */
+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;
+
+  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. */
+  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. */
+    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.
+ */
+int
+base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+  /* XXXX we might want to rewrite this along the lines of base64_decode, if
+   * it ever shows up in the profile. */
+  unsigned int i;
+  size_t nbits, j, bit;
+  char *tmp;
+  nbits = srclen * 5;
+
+  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);
+
+  memset(dest, 0, destlen);
+
+  /* Convert base32 encoded chars to the 5-bit values that they represent. */
+  tmp = tor_malloc_zero(srclen);
+  for (j = 0; j < srclen; ++j) {
+    if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61;
+    else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18;
+    else if (src[j] > 0x40 && src[j] < 0x5B) tmp[j] = src[j] - 0x41;
+    else {
+      log_warn(LD_BUG, "illegal character in base32 encoded string");
+      tor_free(tmp);
+      return -1;
+    }
+  }
+
+  /* Assemble result byte-wise by applying five possible cases. */
+  for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) {
+    switch (bit % 40) {
+    case 0:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 3) +
+                (((uint8_t)tmp[(bit/5)+1]) >> 2);
+      break;
+    case 8:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 6) +
+                (((uint8_t)tmp[(bit/5)+1]) << 1) +
+                (((uint8_t)tmp[(bit/5)+2]) >> 4);
+      break;
+    case 16:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 4) +
+                (((uint8_t)tmp[(bit/5)+1]) >> 1);
+      break;
+    case 24:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 7) +
+                (((uint8_t)tmp[(bit/5)+1]) << 2) +
+                (((uint8_t)tmp[(bit/5)+2]) >> 3);
+      break;
+    case 32:
+      dest[i] = (((uint8_t)tmp[(bit/5)]) << 5) +
+                ((uint8_t)tmp[(bit/5)+1]);
+      break;
+    }
+  }
+
+  memset(tmp, 0, srclen); /* on the heap, this should be safe */
+  tor_free(tmp);
+  tmp = NULL;
+  return 0;
+}
+
+#define BASE64_OPENSSL_LINELEN 64
+
+/** Return the Base64 encoded size of <b>srclen</b> bytes of data in
+ * bytes.
+ *
+ * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return the size
+ * of the encoded output as multiline output (64 character, `\n' terminated
+ * lines).
+ */
+size_t
+base64_encode_size(size_t srclen, int flags)
+{
+  size_t enclen;
+  tor_assert(srclen < INT_MAX);
+
+  if (srclen == 0)
+    return 0;
+
+  enclen = ((srclen - 1) / 3) * 4 + 4;
+  if (flags & BASE64_ENCODE_MULTILINE) {
+    size_t remainder = enclen % BASE64_OPENSSL_LINELEN;
+    enclen += enclen / BASE64_OPENSSL_LINELEN;
+    if (remainder)
+      enclen++;
+  }
+  tor_assert(enclen < INT_MAX && enclen > srclen);
+  return enclen;
+}
+
+/** Internal table mapping 6 bit values to the Base64 alphabet. */
+static const char base64_encode_table[64] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+  'w', 'x', 'y', 'z', '0', '1', '2', '3',
+  '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+/** Base64 encode <b>srclen</b> bytes of data from <b>src</b>.  Write
+ * the result into <b>dest</b>, if it will fit within <b>destlen</b>
+ * bytes. Return the number of bytes written on success; -1 if
+ * destlen is too short, or other failure.
+ *
+ * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return encoded
+ * output in multiline format (64 character, `\n' terminated lines).
+ */
+int
+base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
+              int flags)
+{
+  const unsigned char *usrc = (unsigned char *)src;
+  const unsigned char *eous = usrc + srclen;
+  char *d = dest;
+  uint32_t n = 0;
+  size_t linelen = 0;
+  size_t enclen;
+  int n_idx = 0;
+
+  if (!src || !dest)
+    return -1;
+
+  /* Ensure that there is sufficient space, including the NUL. */
+  enclen = base64_encode_size(srclen, flags);
+  if (destlen < enclen + 1)
+    return -1;
+  if (destlen > SIZE_T_CEILING)
+    return -1;
+  if (enclen > INT_MAX)
+    return -1;
+
+  memset(dest, 0, enclen);
+
+  /* XXX/Yawning: If this ends up being too slow, this can be sped up
+   * by separating the multiline format case and the normal case, and
+   * processing 48 bytes of input at a time when newlines are desired.
+   */
+#define ENCODE_CHAR(ch) \
+  STMT_BEGIN                                                    \
+    *d++ = ch;                                                  \
+    if (flags & BASE64_ENCODE_MULTILINE) {                      \
+      if (++linelen % BASE64_OPENSSL_LINELEN == 0) {            \
+        linelen = 0;                                            \
+        *d++ = '\n';                                            \
+      }                                                         \
+    }                                                           \
+  STMT_END
+
+#define ENCODE_N(idx) \
+  ENCODE_CHAR(base64_encode_table[(n >> ((3 - idx) * 6)) & 0x3f])
+
+#define ENCODE_PAD() ENCODE_CHAR('=')
+
+  /* Iterate over all the bytes in src.  Each one will add 8 bits to the
+   * value we're encoding.  Accumulate bits in <b>n</b>, and whenever we
+   * have 24 bits, batch them into 4 bytes and flush those bytes to dest.
+   */
+  for ( ; usrc < eous; ++usrc) {
+    n = (n << 8) | *usrc;
+    if ((++n_idx) == 3) {
+      ENCODE_N(0);
+      ENCODE_N(1);
+      ENCODE_N(2);
+      ENCODE_N(3);
+      n_idx = 0;
+      n = 0;
+    }
+  }
+  switch (n_idx) {
+  case 0:
+    /* 0 leftover bits, no pading to add. */
+    break;
+  case 1:
+    /* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed
+     * by 2 padding characters.
+     */
+    n <<= 4;
+    ENCODE_N(2);
+    ENCODE_N(3);
+    ENCODE_PAD();
+    ENCODE_PAD();
+    break;
+  case 2:
+    /* 16 leftover bits, pad to 18 bits, write the 3 6-bit values followed
+     * by 1 padding character.
+     */
+    n <<= 2;
+    ENCODE_N(1);
+    ENCODE_N(2);
+    ENCODE_N(3);
+    ENCODE_PAD();
+    break;
+  default:
+    /* Something went catastrophically wrong. */
+    tor_fragile_assert();
+    return -1;
+  }
+
+#undef ENCODE_N
+#undef ENCODE_PAD
+#undef ENCODE_CHAR
+
+  /* Multiline output always includes at least one newline. */
+  if (flags & BASE64_ENCODE_MULTILINE && linelen != 0)
+    *d++ = '\n';
+
+  tor_assert(d - dest == (ptrdiff_t)enclen);
+
+  *d++ = '\0'; /* NUL terminate the output. */
+
+  return (int) enclen;
+}
+
+/** As base64_encode, but do not add any internal spaces or external padding
+ * to the output stream. */
+int
+base64_encode_nopad(char *dest, size_t destlen,
+                    const uint8_t *src, size_t srclen)
+{
+  int n = base64_encode(dest, destlen, (const char*) src, srclen, 0);
+  if (n <= 0)
+    return n;
+  tor_assert((size_t)n < destlen && dest[n] == 0);
+  char *in, *out;
+  in = out = dest;
+  while (*in) {
+    if (*in == '=' || *in == '\n') {
+      ++in;
+    } else {
+      *out++ = *in++;
+    }
+  }
+  *out = 0;
+
+  tor_assert(out - dest <= INT_MAX);
+
+  return (int)(out - dest);
+}
+
+/** As base64_decode, but do not require any padding on the input */
+int
+base64_decode_nopad(uint8_t *dest, size_t destlen,
+                    const char *src, size_t srclen)
+{
+  if (srclen > SIZE_T_CEILING - 4)
+    return -1;
+  char *buf = tor_malloc(srclen + 4);
+  memcpy(buf, src, srclen+1);
+  size_t buflen;
+  switch (srclen % 4)
+    {
+    case 0:
+    default:
+      buflen = srclen;
+      break;
+    case 1:
+      tor_free(buf);
+      return -1;
+    case 2:
+      memcpy(buf+srclen, "==", 3);
+      buflen = srclen + 2;
+      break;
+    case 3:
+      memcpy(buf+srclen, "=", 2);
+      buflen = srclen + 1;
+      break;
+  }
+  int n = base64_decode((char*)dest, destlen, buf, buflen);
+  tor_free(buf);
+  return n;
+}
+
+#undef BASE64_OPENSSL_LINELEN
+
+/** @{ */
+/** Special values used for the base64_decode_table */
+#define X 255
+#define SP 64
+#define PAD 65
+/** @} */
+/** Internal table mapping byte values to what they represent in base64.
+ * Numbers 0..63 are 6-bit integers.  SPs are spaces, and should be
+ * skipped.  Xs are invalid and must not appear in base64. PAD indicates
+ * end-of-string. */
+static const uint8_t base64_decode_table[256] = {
+  X, X, X, X, X, X, X, X, X, SP, SP, SP, X, SP, X, X, /* */
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  SP, X, X, X, X, X, X, X, X, X, X, 62, X, X, X, 63,
+  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, X, X, X, PAD, X, X,
+  X, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, X, X, X, X, X,
+  X, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
+};
+
+/** Base64 decode <b>srclen</b> bytes of data from <b>src</b>.  Write
+ * the result into <b>dest</b>, if it will fit within <b>destlen</b>
+ * bytes.  Return the number of bytes written on success; -1 if
+ * destlen is too short, or other failure.
+ *
+ * NOTE 1: destlen is checked conservatively, as though srclen contained no
+ * spaces or padding.
+ *
+ * NOTE 2: This implementation does not check for the correct number of
+ * padding "=" characters at the end of the string, and does not check
+ * for internal padding characters.
+ */
+int
+base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+  const char *eos = src+srclen;
+  uint32_t n=0;
+  int n_idx=0;
+  char *dest_orig = dest;
+
+  /* Max number of bits == srclen*6.
+   * Number of bytes required to hold all bits == (srclen*6)/8.
+   * Yes, we want to round down: anything that hangs over the end of a
+   * byte is padding. */
+  if (destlen < (srclen*3)/4)
+    return -1;
+  if (destlen > SIZE_T_CEILING)
+    return -1;
+
+  memset(dest, 0, destlen);
+
+  /* Iterate over all the bytes in src.  Each one will add 0 or 6 bits to the
+   * value we're decoding.  Accumulate bits in <b>n</b>, and whenever we have
+   * 24 bits, batch them into 3 bytes and flush those bytes to dest.
+   */
+  for ( ; src < eos; ++src) {
+    unsigned char c = (unsigned char) *src;
+    uint8_t v = base64_decode_table[c];
+    switch (v) {
+      case X:
+        /* This character isn't allowed in base64. */
+        return -1;
+      case SP:
+        /* This character is whitespace, and has no effect. */
+        continue;
+      case PAD:
+        /* We've hit an = character: the data is over. */
+        goto end_of_loop;
+      default:
+        /* We have an actual 6-bit value.  Append it to the bits in n. */
+        n = (n<<6) | v;
+        if ((++n_idx) == 4) {
+          /* We've accumulated 24 bits in n. Flush them. */
+          *dest++ = (n>>16);
+          *dest++ = (n>>8) & 0xff;
+          *dest++ = (n) & 0xff;
+          n_idx = 0;
+          n = 0;
+        }
+    }
+  }
+ end_of_loop:
+  /* If we have leftover bits, we need to cope. */
+  switch (n_idx) {
+    case 0:
+    default:
+      /* No leftover bits.  We win. */
+      break;
+    case 1:
+      /* 6 leftover bits. That's invalid; we can't form a byte out of that. */
+      return -1;
+    case 2:
+      /* 12 leftover bits: The last 4 are padding and the first 8 are data. */
+      *dest++ = n >> 4;
+      break;
+    case 3:
+      /* 18 leftover bits: The last 2 are padding and the first 16 are data. */
+      *dest++ = n >> 10;
+      *dest++ = n >> 2;
+  }
+
+  tor_assert((dest-dest_orig) <= (ssize_t)destlen);
+  tor_assert((dest-dest_orig) <= INT_MAX);
+
+  return (int)(dest-dest_orig);
+}
+#undef X
+#undef SP
+#undef PAD
+
+/** Encode the <b>srclen</b> bytes at <b>src</b> in a NUL-terminated,
+ * uppercase hexadecimal string; store it in the <b>destlen</b>-byte buffer
+ * <b>dest</b>.
+ */
+void
+base16_encode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+  const char *end;
+  char *cp;
+
+  tor_assert(destlen >= srclen*2+1);
+  tor_assert(destlen < SIZE_T_CEILING);
+
+  cp = dest;
+  end = src+srclen;
+  while (src<end) {
+    *cp++ = "0123456789ABCDEF"[ (*(const uint8_t*)src) >> 4 ];
+    *cp++ = "0123456789ABCDEF"[ (*(const uint8_t*)src) & 0xf ];
+    ++src;
+  }
+  *cp = '\0';
+}
+
+/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
+static INLINE int
+hex_decode_digit_(char c)
+{
+  switch (c) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'A': case 'a': return 10;
+    case 'B': case 'b': return 11;
+    case 'C': case 'c': return 12;
+    case 'D': case 'd': return 13;
+    case 'E': case 'e': return 14;
+    case 'F': case 'f': return 15;
+    default:
+      return -1;
+  }
+}
+
+/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
+int
+hex_decode_digit(char c)
+{
+  return hex_decode_digit_(c);
+}
+
+/** Given a hexadecimal string of <b>srclen</b> bytes in <b>src</b>, decode it
+ * and store the result in the <b>destlen</b>-byte buffer at <b>dest</b>.
+ * Return 0 on success, -1 on failure. */
+int
+base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+  const char *end;
+
+  int v1,v2;
+  if ((srclen % 2) != 0)
+    return -1;
+  if (destlen < srclen/2 || destlen > SIZE_T_CEILING)
+    return -1;
+
+  memset(dest, 0, destlen);
+
+  end = src+srclen;
+  while (src<end) {
+    v1 = hex_decode_digit_(*src);
+    v2 = hex_decode_digit_(*(src+1));
+    if (v1<0||v2<0)
+      return -1;
+    *(uint8_t*)dest = (v1<<4)|v2;
+    ++dest;
+    src+=2;
+  }
+  return 0;
+}
+

+ 33 - 0
src/common/util_format.h

@@ -0,0 +1,33 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_UTIL_FORMAT_H
+#define TOR_UTIL_FORMAT_H
+
+#include "testsupport.h"
+#include "torint.h"
+
+#define BASE64_ENCODE_MULTILINE 1
+size_t base64_encode_size(size_t srclen, int flags);
+int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
+                  int flags);
+int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+int base64_encode_nopad(char *dest, size_t destlen,
+                        const uint8_t *src, size_t srclen);
+int base64_decode_nopad(uint8_t *dest, size_t destlen,
+                        const char *src, size_t srclen);
+
+/** Characters that can appear (case-insensitively) in a base32 encoding. */
+#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);
+
+int hex_decode_digit(char c);
+void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
+int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+
+#endif
+

+ 2 - 0
src/or/keypin.c

@@ -6,6 +6,7 @@
 #include "orconfig.h"
 #include "compat.h"
 #include "crypto.h"
+#include "crypto_format.h"
 #include "di_ops.h"
 #include "ht.h"
 #include "keypin.h"
@@ -13,6 +14,7 @@
 #include "torint.h"
 #include "torlog.h"
 #include "util.h"
+#include "util_format.h"
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>

+ 2 - 0
src/or/or.h

@@ -81,6 +81,7 @@
 #endif
 
 #include "crypto.h"
+#include "crypto_format.h"
 #include "tortls.h"
 #include "torlog.h"
 #include "container.h"
@@ -92,6 +93,7 @@
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
 #include "tor_queue.h"
+#include "util_format.h"
 
 /* These signals are defined to help handle_control_signal work.
  */

+ 1 - 0
src/tools/tor-gencert.c

@@ -32,6 +32,7 @@
 #include "torlog.h"
 #include "crypto.h"
 #include "address.h"
+#include "util_format.h"
 
 #define IDENTITY_KEY_BITS 3072
 #define SIGNING_KEY_BITS 2048