From 3ea5399aeec092dcdb9f039288f1e146aadbb2f7 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 10 Jun 2014 17:52:22 +0200 Subject: [PATCH] Move base64 implementation to own file. The base64 decoder is independent of anything else, so it should not be part of the crypto API. This patch moves the two public functions uudecode() and base64_decode() to a new file, base64.c, and introduces base64.h to declare them. --- base64.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ base64.h | 2 + configure.ac | 3 + crypt.c | 1 + crypt_backend.h | 2 - crypt_common.c | 160 ---------------------------------------------- error.h | 3 +- gcrypt.c | 1 + 8 files changed, 176 insertions(+), 163 deletions(-) create mode 100644 base64.c create mode 100644 base64.h diff --git a/base64.c b/base64.c new file mode 100644 index 00000000..e9892244 --- /dev/null +++ b/base64.c @@ -0,0 +1,167 @@ +/* + * The code in this file was taken from openssh-5.2p1, Copyright (c) 1996 by + * Internet Software Consortium. Portions Copyright (c) 1995 by International + * Business Machines, Inc. + */ + +/** \file base64.c Uudecode and base64decode implementation. */ + +#include + +#include "para.h" +#include "error.h" +#include "base64.h" +#include "string.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/** + * base64-decode a buffer. + * + * \param src The buffer to decode. + * \param target Result is stored here. + * \param targsize Number of bytes of \a target. + * + * Skips all whitespace anywhere. Converts characters, four at a time, starting + * at (or after) src from base - 64 numbers into three 8 bit bytes in the + * target area. + * + * \return The number of data bytes stored at the target, -E_BASE64 on errors. + */ +int base64_decode(char const *src, unsigned char *target, size_t targsize) +{ + unsigned int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (para_isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == NULL) /* A non-base64 character. */ + return -E_BASE64; + + switch (state) { + case 0: + if (tarindex >= targsize) + return -E_BASE64; + target[tarindex] = (pos - Base64) << 2; + state = 1; + break; + case 1: + if (tarindex + 1 >= targsize) + return -E_BASE64; + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4; + tarindex++; + state = 2; + break; + case 2: + if (tarindex + 1 >= targsize) + return -E_BASE64; + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex + 1] = ((pos - Base64) & 0x03) << 6; + tarindex++; + state = 3; + break; + case 3: + if (tarindex >= targsize) + return -E_BASE64; + target[tarindex] |= pos - Base64; + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return -E_BASE64; + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return -E_BASE64; + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return -E_BASE64; + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target[tarindex] != 0) + return -E_BASE64; + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return -E_BASE64; + } + return tarindex; +} + +/** + * uudecode a buffer. + * + * \param src The buffer to decode. + * \param target Result buffer. + * \param targsize The length of \a target in bytes. + * + * This is just a simple wrapper for base64_decode() which strips whitespace. + * + * \return The return value of the underlying call to base64_decode(). + */ +int uudecode(const char *src, unsigned char *target, size_t targsize) +{ + int len; + char *encoded, *p; + + /* copy the 'readonly' source */ + encoded = para_strdup(src); + /* skip whitespace and data */ + for (p = encoded; *p == ' ' || *p == '\t'; p++) + ; + for (; *p != '\0' && *p != ' ' && *p != '\t'; p++) + ; + /* and remove trailing whitespace because base64_decode needs this */ + *p = '\0'; + len = base64_decode(encoded, target, targsize); + free(encoded); + return len; +} diff --git a/base64.h b/base64.h new file mode 100644 index 00000000..a06bf2ec --- /dev/null +++ b/base64.h @@ -0,0 +1,2 @@ +int uudecode(const char *src, unsigned char *target, size_t targsize); +int base64_decode(char const *src, unsigned char *target, size_t targsize); diff --git a/configure.ac b/configure.ac index f9115fd6..8d4ce59a 100644 --- a/configure.ac +++ b/configure.ac @@ -418,6 +418,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then close_on_fork mm crypt_common + base64 ipc dccp_send fd @@ -480,6 +481,7 @@ if test -n "$CRYPTOLIB"; then client_common buffer_tree crypt_common + base64 version ggo " @@ -523,6 +525,7 @@ if test -n "$CRYPTOLIB"; then stat net crypt_common + base64 sideband time grab_client diff --git a/crypt.c b/crypt.c index 610d2057..222bece7 100644 --- a/crypt.c +++ b/crypt.c @@ -23,6 +23,7 @@ #include "crypt.h" #include "fd.h" #include "crypt_backend.h" +#include "base64.h" struct asymmetric_key { RSA *rsa; diff --git a/crypt_backend.h b/crypt_backend.h index 06c86d74..f9a69d94 100644 --- a/crypt_backend.h +++ b/crypt_backend.h @@ -13,7 +13,5 @@ size_t is_ssh_rsa_key(char *data, size_t size); uint32_t read_ssh_u32(const void *vp); -int uudecode(const char *src, unsigned char *target, size_t targsize); int check_ssh_key_header(const unsigned char *blob, int blen); int check_key_file(const char *file, bool private_key); -int base64_decode(char const *src, unsigned char *target, size_t targsize); diff --git a/crypt_common.c b/crypt_common.c index 022692ad..b39ee5e4 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -45,166 +45,6 @@ size_t is_ssh_rsa_key(char *data, size_t size) return cp - data; } -/* - * This base64/uudecode stuff below is taken from openssh-5.2p1, Copyright (c) - * 1996 by Internet Software Consortium. Portions Copyright (c) 1995 by - * International Business Machines, Inc. - */ - -static const char Base64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - -/** - * base64-decode a buffer. - * - * \param src The buffer to decode. - * \param target Result is stored here. - * \param targsize Number of bytes of \a target. - * - * Skips all whitespace anywhere. Converts characters, four at a time, starting - * at (or after) src from base - 64 numbers into three 8 bit bytes in the - * target area. - * - * \return The number of data bytes stored at the target, -E_BASE64 on errors. - */ -int base64_decode(char const *src, unsigned char *target, size_t targsize) -{ - unsigned int tarindex, state; - int ch; - char *pos; - - state = 0; - tarindex = 0; - - while ((ch = *src++) != '\0') { - if (para_isspace(ch)) /* Skip whitespace anywhere. */ - continue; - - if (ch == Pad64) - break; - - pos = strchr(Base64, ch); - if (pos == NULL) /* A non-base64 character. */ - return -E_BASE64; - - switch (state) { - case 0: - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] = (pos - Base64) << 2; - state = 1; - break; - case 1: - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 4; - target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4; - tarindex++; - state = 2; - break; - case 2: - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 2; - target[tarindex + 1] = ((pos - Base64) & 0x03) << 6; - tarindex++; - state = 3; - break; - case 3: - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] |= pos - Base64; - tarindex++; - state = 0; - break; - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return -E_BASE64; - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) - break; - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return -E_BASE64; - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) - return -E_BASE64; - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target[tarindex] != 0) - return -E_BASE64; - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) - return -E_BASE64; - } - - return tarindex; -} - -/** - * uudecode a buffer. - * - * \param src The buffer to decode. - * \param target Result buffer. - * \param targsize The length of \a target in bytes. - * - * This is just a simple wrapper for base64_decode() which strips whitespace. - * - * \return The return value of the underlying call to base64_decode(). - */ -int uudecode(const char *src, unsigned char *target, size_t targsize) -{ - int len; - char *encoded, *p; - - /* copy the 'readonly' source */ - encoded = para_strdup(src); - /* skip whitespace and data */ - for (p = encoded; *p == ' ' || *p == '\t'; p++) - ; - for (; *p != '\0' && *p != ' ' && *p != '\t'; p++) - ; - /* and remove trailing whitespace because base64_decode needs this */ - *p = '\0'; - len = base64_decode(encoded, target, targsize); - free(encoded); - return len; -} - /** * Read a 4-byte number from a buffer in big-endian format. * diff --git a/error.h b/error.h index 337160c9..ea12469b 100644 --- a/error.h +++ b/error.h @@ -440,9 +440,10 @@ extern const char **para_errlist[]; #define CRYPT_COMMON_ERRORS \ PARA_ERROR(SSH_KEY_HEADER, "ssh key header not found"), \ - PARA_ERROR(BASE64, "failed to base64-decode ssh public key"), \ PARA_ERROR(KEY_PERM, "unprotected private key"), \ +#define BASE64_ERRORS \ + PARA_ERROR(BASE64, "base64 decode error"), \ #define CRYPT_ERRORS \ PARA_ERROR(PRIVATE_KEY, "can not read private key"), \ diff --git a/gcrypt.c b/gcrypt.c index 3c6c1ad1..8e73b3b2 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -15,6 +15,7 @@ #include "crypt.h" #include "crypt_backend.h" #include "fd.h" +#include "base64.h" //#define GCRYPT_DEBUG 1 -- 2.39.5