From 76c0ef86e40de386124252c2375d6f49389eb0d5 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Wed, 19 Aug 2015 11:22:30 -0700 Subject: [PATCH 1/3] Move base64 and hex functions to string_util --- src/common_auth.cpp | 98 +-------------------------------------------- src/s3fs_auth.h | 6 +-- src/string_util.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++ src/string_util.h | 4 ++ 4 files changed, 106 insertions(+), 100 deletions(-) diff --git a/src/common_auth.cpp b/src/common_auth.cpp index 96bf00d..778a5ab 100644 --- a/src/common_auth.cpp +++ b/src/common_auth.cpp @@ -25,109 +25,13 @@ #include #include "s3fs_auth.h" +#include "string_util.h" using namespace std; //------------------------------------------------------------------- // Utility Function //------------------------------------------------------------------- -std::string s3fs_hex(const unsigned char* input, size_t length) -{ - std::string hex; - for(size_t pos = 0; pos < length; ++pos){ - char hexbuf[3]; - snprintf(hexbuf, 3, "%02x", input[pos]); - hex += hexbuf; - } - return hex; -} - -char* s3fs_base64(const unsigned char* input, size_t length) -{ - static const char* base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - char* result; - - if(!input || 0 >= length){ - return NULL; - } - if(NULL == (result = (char*)malloc((((length / 3) + 1) * 4 + 1) * sizeof(char)))){ - return NULL; // ENOMEM - } - - unsigned char parts[4]; - size_t rpos; - size_t wpos; - for(rpos = 0, wpos = 0; rpos < length; rpos += 3){ - parts[0] = (input[rpos] & 0xfc) >> 2; - parts[1] = ((input[rpos] & 0x03) << 4) | ((((rpos + 1) < length ? input[rpos + 1] : 0x00) & 0xf0) >> 4); - parts[2] = (rpos + 1) < length ? (((input[rpos + 1] & 0x0f) << 2) | ((((rpos + 2) < length ? input[rpos + 2] : 0x00) & 0xc0) >> 6)) : 0x40; - parts[3] = (rpos + 2) < length ? (input[rpos + 2] & 0x3f) : 0x40; - - result[wpos++] = base[parts[0]]; - result[wpos++] = base[parts[1]]; - result[wpos++] = base[parts[2]]; - result[wpos++] = base[parts[3]]; - } - result[wpos] = '\0'; - - return result; -} - -inline unsigned char char_decode64(const char ch) -{ - unsigned char by; - if('A' <= ch && ch <= 'Z'){ // A - Z - by = static_cast(ch - 'A'); - }else if('a' <= ch && ch <= 'z'){ // a - z - by = static_cast(ch - 'a' + 26); - }else if('0' <= ch && ch <= '9'){ // 0 - 9 - by = static_cast(ch - '0' + 52); - }else if('+' == ch){ // + - by = 62; - }else if('/' == ch){ // / - by = 63; - }else if('=' == ch){ // = - by = 64; - }else{ // something wrong - by = UCHAR_MAX; - } - return by; -} - -unsigned char* s3fs_decode64(const char* input, size_t* plength) -{ - unsigned char* result; - if(!input || 0 == strlen(input) || !plength){ - return NULL; - } - if(NULL == (result = (unsigned char*)malloc((strlen(input) + 1)))){ - return NULL; // ENOMEM - } - - unsigned char parts[4]; - size_t input_len = strlen(input); - size_t rpos; - size_t wpos; - for(rpos = 0, wpos = 0; rpos < input_len; rpos += 4){ - parts[0] = char_decode64(input[rpos]); - parts[1] = (rpos + 1) < input_len ? char_decode64(input[rpos + 1]) : 64; - parts[2] = (rpos + 2) < input_len ? char_decode64(input[rpos + 2]) : 64; - parts[3] = (rpos + 3) < input_len ? char_decode64(input[rpos + 3]) : 64; - - result[wpos++] = ((parts[0] << 2) & 0xfc) | ((parts[1] >> 4) & 0x03); - if(64 == parts[2]){ - break; - } - result[wpos++] = ((parts[1] << 4) & 0xf0) | ((parts[2] >> 2) & 0x0f); - if(64 == parts[3]){ - break; - } - result[wpos++] = ((parts[2] << 6) & 0xc0) | (parts[3] & 0x3f); - } - *plength = wpos; - return result; -} - string s3fs_get_content_md5(int fd) { unsigned char* md5hex; diff --git a/src/s3fs_auth.h b/src/s3fs_auth.h index 250f84d..98fc763 100644 --- a/src/s3fs_auth.h +++ b/src/s3fs_auth.h @@ -20,15 +20,15 @@ #ifndef S3FS_AUTH_H_ #define S3FS_AUTH_H_ +#include +#include + //------------------------------------------------------------------- // Utility functions for Authentication //------------------------------------------------------------------- // // in common_auth.cpp // -std::string s3fs_hex(const unsigned char* input, size_t length); -char* s3fs_base64(const unsigned char* input, size_t length); -unsigned char* s3fs_decode64(const char* input, size_t* plength); std::string s3fs_get_content_md5(int fd); std::string s3fs_md5sum(int fd, off_t start, ssize_t size); std::string s3fs_sha256sum(int fd, off_t start, ssize_t size); diff --git a/src/string_util.cpp b/src/string_util.cpp index 1f5d845..2428149 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include #include #include @@ -268,6 +269,103 @@ string get_date_iso8601(time_t tm) return buf; } +std::string s3fs_hex(const unsigned char* input, size_t length) +{ + std::string hex; + for(size_t pos = 0; pos < length; ++pos){ + char hexbuf[3]; + snprintf(hexbuf, 3, "%02x", input[pos]); + hex += hexbuf; + } + return hex; +} + +char* s3fs_base64(const unsigned char* input, size_t length) +{ + static const char* base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + char* result; + + if(!input || 0 >= length){ + return NULL; + } + if(NULL == (result = (char*)malloc((((length / 3) + 1) * 4 + 1) * sizeof(char)))){ + return NULL; // ENOMEM + } + + unsigned char parts[4]; + size_t rpos; + size_t wpos; + for(rpos = 0, wpos = 0; rpos < length; rpos += 3){ + parts[0] = (input[rpos] & 0xfc) >> 2; + parts[1] = ((input[rpos] & 0x03) << 4) | ((((rpos + 1) < length ? input[rpos + 1] : 0x00) & 0xf0) >> 4); + parts[2] = (rpos + 1) < length ? (((input[rpos + 1] & 0x0f) << 2) | ((((rpos + 2) < length ? input[rpos + 2] : 0x00) & 0xc0) >> 6)) : 0x40; + parts[3] = (rpos + 2) < length ? (input[rpos + 2] & 0x3f) : 0x40; + + result[wpos++] = base[parts[0]]; + result[wpos++] = base[parts[1]]; + result[wpos++] = base[parts[2]]; + result[wpos++] = base[parts[3]]; + } + result[wpos] = '\0'; + + return result; +} + +inline unsigned char char_decode64(const char ch) +{ + unsigned char by; + if('A' <= ch && ch <= 'Z'){ // A - Z + by = static_cast(ch - 'A'); + }else if('a' <= ch && ch <= 'z'){ // a - z + by = static_cast(ch - 'a' + 26); + }else if('0' <= ch && ch <= '9'){ // 0 - 9 + by = static_cast(ch - '0' + 52); + }else if('+' == ch){ // + + by = 62; + }else if('/' == ch){ // / + by = 63; + }else if('=' == ch){ // = + by = 64; + }else{ // something wrong + by = UCHAR_MAX; + } + return by; +} + +unsigned char* s3fs_decode64(const char* input, size_t* plength) +{ + unsigned char* result; + if(!input || 0 == strlen(input) || !plength){ + return NULL; + } + if(NULL == (result = (unsigned char*)malloc((strlen(input) + 1)))){ + return NULL; // ENOMEM + } + + unsigned char parts[4]; + size_t input_len = strlen(input); + size_t rpos; + size_t wpos; + for(rpos = 0, wpos = 0; rpos < input_len; rpos += 4){ + parts[0] = char_decode64(input[rpos]); + parts[1] = (rpos + 1) < input_len ? char_decode64(input[rpos + 1]) : 64; + parts[2] = (rpos + 2) < input_len ? char_decode64(input[rpos + 2]) : 64; + parts[3] = (rpos + 3) < input_len ? char_decode64(input[rpos + 3]) : 64; + + result[wpos++] = ((parts[0] << 2) & 0xfc) | ((parts[1] >> 4) & 0x03); + if(64 == parts[2]){ + break; + } + result[wpos++] = ((parts[1] << 4) & 0xf0) | ((parts[2] >> 2) & 0x0f); + if(64 == parts[3]){ + break; + } + result[wpos++] = ((parts[2] << 6) & 0xc0) | (parts[3] & 0x3f); + } + *plength = wpos; + return result; +} + /* * Local variables: * tab-width: 4 diff --git a/src/string_util.h b/src/string_util.h index c61141c..29ff04f 100644 --- a/src/string_util.h +++ b/src/string_util.h @@ -55,6 +55,10 @@ std::string urlDecode(const std::string& s); bool takeout_str_dquart(std::string& str); bool get_keyword_value(std::string& target, const char* keyword, std::string& value); +std::string s3fs_hex(const unsigned char* input, size_t length); +char* s3fs_base64(const unsigned char* input, size_t length); +unsigned char* s3fs_decode64(const char* input, size_t* plength); + #endif // S3FS_STRING_UTIL_H_ /* From 15db80b459658b588e877a92ea96d1a10d75302e Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Wed, 19 Aug 2015 13:40:22 -0700 Subject: [PATCH 2/3] NUL terminate decoded base64 string For consistency with encoded strings. --- src/string_util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/string_util.cpp b/src/string_util.cpp index 2428149..4a6d71c 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -362,6 +362,7 @@ unsigned char* s3fs_decode64(const char* input, size_t* plength) } result[wpos++] = ((parts[2] << 6) & 0xc0) | (parts[3] & 0x3f); } + result[wpos] = '\0'; *plength = wpos; return result; } From b5c027f15dda9c891c2b95fa228609b69e3b930c Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Wed, 19 Aug 2015 11:44:02 -0700 Subject: [PATCH 3/3] Add unit tests for base64 encoding and decoding --- src/test_string_util.cpp | 31 ++++++++++++++++++++++++++++++- src/test_util.h | 12 ++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/test_string_util.cpp b/src/test_string_util.cpp index 593a083..2cc372e 100644 --- a/src/test_string_util.cpp +++ b/src/test_string_util.cpp @@ -25,7 +25,7 @@ #include "string_util.h" #include "test_util.h" -int main(int argc, char *argv[]) +void test_trim() { ASSERT_EQUALS(std::string("1234"), trim(" 1234 ")); ASSERT_EQUALS(std::string("1234"), trim("1234 ")); @@ -49,6 +49,35 @@ int main(int argc, char *argv[]) ASSERT_EQUALS(std::string("-9223372036854775808"), str(std::numeric_limits::min())); ASSERT_EQUALS(std::string("0"), str(std::numeric_limits::min())); ASSERT_EQUALS(std::string("18446744073709551615"), str(std::numeric_limits::max())); +} +void test_base64() +{ + size_t len; + ASSERT_STREQUALS(s3fs_base64(NULL, 0), NULL); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64(NULL, &len)), NULL); + ASSERT_STREQUALS(s3fs_base64(reinterpret_cast(""), 0), NULL); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64("", &len)), NULL); + + ASSERT_STREQUALS(s3fs_base64(reinterpret_cast("1"), 1), "MQ=="); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64("MQ==", &len)), "1"); + ASSERT_EQUALS(len, static_cast(1)); + ASSERT_STREQUALS(s3fs_base64(reinterpret_cast("12"), 2), "MTI="); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64("MTI=", &len)), "12"); + ASSERT_EQUALS(len, static_cast(2)); + ASSERT_STREQUALS(s3fs_base64(reinterpret_cast("123"), 3), "MTIz"); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64("MTIz", &len)), "123"); + ASSERT_EQUALS(len, static_cast(3)); + ASSERT_STREQUALS(s3fs_base64(reinterpret_cast("1234"), 4), "MTIzNA=="); + ASSERT_STREQUALS(reinterpret_cast(s3fs_decode64("MTIzNA==", &len)), "1234"); + ASSERT_EQUALS(len, static_cast(4)); + + // TODO: invalid input +} + +int main(int argc, char *argv[]) +{ + test_trim(); + test_base64(); return 0; } diff --git a/src/test_util.h b/src/test_util.h index 2422bd0..7180375 100644 --- a/src/test_util.h +++ b/src/test_util.h @@ -29,6 +29,18 @@ template void assert_equals(const T &x, const T &y, const char *fil } } +void assert_strequals(const char *x, const char *y, const char *file, int line) +{ + if(x == NULL && y == NULL){ + return; + } else if((x == NULL || y == NULL) || strcmp(x, y) != 0){ + std::cerr << x << " != " << y << " at " << file << ":" << line << std::endl; + std::exit(1); + } +} + #define ASSERT_EQUALS(x, y) \ assert_equals((x), (y), __FILE__, __LINE__) +#define ASSERT_STREQUALS(x, y) \ + assert_strequals((x), (y), __FILE__, __LINE__)