diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index ef723dd3b..844a73ec0 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -926,17 +926,18 @@ uint32_t srs_crc32_mpegts(const void* buf, int size) return __crc32_table_driven(__crc32_MPEG_table, buf, size, 0x00, reflect_in, xor_in, reflect_out, xor_out); } +// We use the standard encoding: +// var StdEncoding = NewEncoding(encodeStd) +// StdEncoding is the standard base64 encoding, as defined in RFC 4648. +namespace { + char padding = '='; + string encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +} // @see golang encoding/base64/base64.go srs_error_t srs_av_base64_decode(string cipher, string& plaintext) { srs_error_t err = srs_success; - // We use the standard encoding: - // var StdEncoding = NewEncoding(encodeStd) - // StdEncoding is the standard base64 encoding, as defined in RFC 4648. - char padding = '='; - string encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - uint8_t decodeMap[256]; memset(decodeMap, 0xff, sizeof(decodeMap)); @@ -1036,6 +1037,67 @@ srs_error_t srs_av_base64_decode(string cipher, string& plaintext) return err; } +// @see golang encoding/base64/base64.go +srs_error_t srs_av_base64_encode(std::string plaintext, std::string& cipher) +{ + srs_error_t err = srs_success; + uint8_t decodeMap[256]; + memset(decodeMap, 0xff, sizeof(decodeMap)); + + for (int i = 0; i < (int)encoder.length(); i++) { + decodeMap[(uint8_t)encoder.at(i)] = uint8_t(i); + } + cipher.clear(); + + uint32_t val = 0; + int di = 0; + int si = 0; + int n = (plaintext.length() / 3) * 3; + uint8_t* p = (uint8_t*)plaintext.c_str(); + while(si < n) { + // Convert 3x 8bit source bytes into 4 bytes + uint32_t v1 = uint32_t(p[si+0]) << 16; + uint32_t v2 = uint32_t(p[si+1]) << 8; + uint32_t v3 = uint32_t(p[si+2]); + val = (uint32_t(p[si + 0]) << 16) | (uint32_t(p[si + 1])<< 8) | uint32_t(p[si + 2]); + + cipher += encoder[val>>18&0x3f]; + cipher += encoder[val>>12&0x3f]; + cipher += encoder[val>>6&0x3f]; + cipher += encoder[val&0x3f]; + + si += 3; + di += 4; + } + + int remain = plaintext.length() - si; + if(0 == remain) { + return err; + } + + val = uint32_t(p[si + 0]) << 16; + if( 2 == remain) { + val |= uint32_t(p[si + 1]) << 8; + } + + cipher += encoder[val>>18&0x3f]; + cipher += encoder[val>>12&0x3f]; + + switch (remain) { + case 2: + cipher += encoder[val>>6&0x3f]; + cipher += padding; + break; + case 1: + cipher += padding; + cipher += padding; + break; + } + + + return err; +} + #define SPACE_CHARS " \t\r\n" int av_toupper(int c) diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index dbab921c0..586878ca4 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -141,6 +141,8 @@ extern uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous = 0) // Decode a base64-encoded string. extern srs_error_t srs_av_base64_decode(std::string cipher, std::string& plaintext); +// Encode a plaintext to base64-encoded string. +extern srs_error_t srs_av_base64_encode(std::string plaintext, std::string& cipher); // Calculate the output size needed to base64-encode x bytes to a null-terminated string. #define SRS_AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 13caea980..9c791afd2 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -2393,6 +2393,89 @@ VOID TEST(KernelUtility, Base64Decode) EXPECT_TRUE(expect == plaintext); } +VOID TEST(KernelUtility, Base64Encode) +{ + srs_error_t err; + + string expect = "dXNlcjpwYXNzd29yZA=="; + string plaintext = "user:password"; + + string cipher; + HELPER_EXPECT_SUCCESS(srs_av_base64_encode(plaintext, cipher)); + EXPECT_TRUE(expect == cipher); +} + +VOID TEST(KernelUtility, Base64) +{ + srs_error_t err = srs_success; + struct testpair { + string decoded; + string encoded; + }; + + struct testpair data[] = { + // RFC 3548 examples + {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"}, + {"\x14\xfb\x9c\x03\xd9", "FPucA9k="}, + {"\x14\xfb\x9c\x03", "FPucAw=="}, + + // RFC 4648 examples + {"", ""}, + {"f", "Zg=="}, + {"fo", "Zm8="}, + {"foo", "Zm9v"}, + {"foob", "Zm9vYg=="}, + {"fooba", "Zm9vYmE="}, + {"foobar", "Zm9vYmFy"}, + + // Wikipedia examples + {"sure.", "c3VyZS4="}, + {"sure", "c3VyZQ=="}, + {"sur", "c3Vy"}, + {"su", "c3U="}, + {"leasure.", "bGVhc3VyZS4="}, + {"easure.", "ZWFzdXJlLg=="}, + {"asure.", "YXN1cmUu"}, + {"sure.", "c3VyZS4="}, + {"Twas brillig, and the slithy toves", "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw=="} + }; + + for(int i = 0; i < (sizeof(data) / sizeof(struct testpair)); ++i) { + struct testpair& d = data[i]; + string cipher; + HELPER_EXPECT_SUCCESS(srs_av_base64_encode(d.decoded, cipher)); + EXPECT_STREQ(d.encoded.c_str(), cipher.c_str()); + + string plaintext; + HELPER_EXPECT_SUCCESS(srs_av_base64_decode(d.encoded, plaintext)); + EXPECT_STREQ(d.decoded.c_str(), plaintext.c_str()); + } + + string expected = "sure"; + string examples[11] = { + "c3VyZQ==", + "c3VyZQ==\r", + "c3VyZQ==\n", + "c3VyZQ==\r\n", + "c3VyZ\r\nQ==", + "c3V\ryZ\nQ==", + "c3V\nyZ\rQ==", + "c3VyZ\nQ==", + "c3VyZQ\n==", + "c3VyZQ=\n=", + "c3VyZQ=\r\n\r\n=", + }; + + for(int i = 0; i < 11; ++i) { + string& encoded_str = examples[i]; + string plaintext; + HELPER_EXPECT_SUCCESS(srs_av_base64_decode(encoded_str, plaintext)); + EXPECT_STREQ(expected.c_str(), plaintext.c_str()); + } + + +} + VOID TEST(KernelUtility, StringToHex) { if (true) {