diff --git a/src/common/address.c b/src/common/address.c index 80f65e916f..dd336257ef 100644 --- a/src/common/address.c +++ b/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" diff --git a/src/common/crypto.c b/src/common/crypto.c index 88a23f5b14..2121383c75 100644 --- a/src/common/crypto.c +++ b/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 srclen bytes of data in - * bytes. - * - * If flags&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 srclen bytes of data from src. Write - * the result into dest, if it will fit within destlen - * bytes. Return the number of bytes written on success; -1 if - * destlen is too short, or other failure. - * - * If flags&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 n, 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 srclen bytes of data from src. Write - * the result into dest, if it will fit within destlen - * 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 n, 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 digest, remove the trailing = - * characters, and store the nul-terminated result in the first - * BASE64_DIGEST_LEN+1 bytes of d64. */ -/* 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 d64 (without - * trailing newline or = characters), decode it and store the result in the - * first DIGEST_LEN bytes at digest. */ -/* 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 digest, remove the - * trailing = characters, and store the nul-terminated result in the first - * BASE64_DIGEST256_LEN+1 bytes of d64. */ - /* 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 d64 (without - * trailing newline or = characters), decode it and store the result in the - * first DIGEST256_LEN bytes at digest. */ -/* 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> (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 sz bytes of data stored at mem, setting them to * the value byte. diff --git a/src/common/crypto.h b/src/common/crypto.h index b953ab93e7..368e9d8e32 100644 --- a/src/common/crypto.h +++ b/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); diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c index 043782423f..ac0b08a552 100644 --- a/src/common/crypto_curve25519.c +++ b/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 datalen bytes from data to the file named - * fname in the tagged-data format. This format contains a - * 32-byte header, followed by the data itself. The header is the - * NUL-padded string "== typestring: tag ==". The length - * of typestring and tag 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 fname into the - * data_out_len-byte buffer in data_out. Check that the - * typestring matches typestring; store the tag into a newly allocated - * string in tag_out. 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, diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h index d53743986c..d868b3918b 100644 --- a/src/common/crypto_curve25519.h +++ b/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); diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index 1606d02c48..3a629322cb 100644 --- a/src/common/crypto_ed25519.c +++ b/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" diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index d942461cfe..bdac12eb27 100644 --- a/src/common/crypto_ed25519.h +++ b/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, diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c index e825132cb9..d4ecd5b192 100644 --- a/src/common/crypto_format.c +++ b/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 #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 datalen bytes from data to the file named + * fname in the tagged-data format. This format contains a + * 32-byte header, followed by the data itself. The header is the + * NUL-padded string "== typestring: tag ==". The length + * of typestring and tag 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 fname into the + * data_out_len-byte buffer in data_out. Check that the + * typestring matches typestring; store the tag into a newly allocated + * string in tag_out. 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 digest, remove the trailing = + * characters, and store the nul-terminated result in the first + * BASE64_DIGEST_LEN+1 bytes of d64. */ +/* 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 d64 (without + * trailing newline or = characters), decode it and store the result in the + * first DIGEST_LEN bytes at digest. */ +/* 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 digest, remove the + * trailing = characters, and store the nul-terminated result in the first + * BASE64_DIGEST256_LEN+1 bytes of d64. */ + /* 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 d64 (without + * trailing newline or = characters), decode it and store the result in the + * first DIGEST256_LEN bytes at digest. */ +/* 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; +} + diff --git a/src/common/crypto_format.h b/src/common/crypto_format.h new file mode 100644 index 0000000000..b972d3f509 --- /dev/null +++ b/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 + diff --git a/src/common/include.am b/src/common/include.am index de93131615..7de93ba2ac 100644 --- a/src/common/include.am +++ b/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 diff --git a/src/common/util.c b/src/common/util.c index 1aac4fc3d1..b33c80fd45 100644 --- a/src/common/util.c +++ b/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 @@ -1211,91 +1212,6 @@ tor_parse_uint64(const char *s, int base, uint64_t min, CHECK_STRTOX_RESULT(); } -/** Encode the srclen bytes at src in a NUL-terminated, - * uppercase hexadecimal string; store it in the destlen-byte buffer - * dest. - */ -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> 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 srclen bytes in src, decode it - * and store the result in the destlen-byte buffer at dest. - * 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 (srcs, * surrounded by quotes and using standard C escapes. * diff --git a/src/common/util.h b/src/common/util.h index ac4ebfc6f3..8bb4505e86 100644 --- a/src/common/util.h +++ b/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); diff --git a/src/common/util_format.c b/src/common/util_format.c new file mode 100644 index 0000000000..dc544a6c2e --- /dev/null +++ b/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 +#include +#include + +/** 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> (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 srclen bytes of data in + * bytes. + * + * If flags&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 srclen bytes of data from src. Write + * the result into dest, if it will fit within destlen + * bytes. Return the number of bytes written on success; -1 if + * destlen is too short, or other failure. + * + * If flags&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 n, 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 srclen bytes of data from src. Write + * the result into dest, if it will fit within destlen + * 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 n, 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 srclen bytes at src in a NUL-terminated, + * uppercase hexadecimal string; store it in the destlen-byte buffer + * dest. + */ +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> 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 srclen bytes in src, decode it + * and store the result in the destlen-byte buffer at dest. + * 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 diff --git a/src/or/or.h b/src/or/or.h index cbbfe9ddcd..f6aee13f5b 100644 --- a/src/or/or.h +++ b/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. */ diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 7660be6230..e833aa9ef5 100644 --- a/src/tools/tor-gencert.c +++ b/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