|
| 1 | +/** |
| 2 | + * @brief [Base64 Encoding and |
| 3 | + * Decoding](https://en.wikipedia.org/wiki/Base64) |
| 4 | + * @details In programming, [Base64](https://en.wikipedia.org/wiki/Base64) is a |
| 5 | + * group of binary-to-text encoding schemes that represent binary data (more |
| 6 | + * specifically, a sequence of 8-bit bytes) in an ASCII string format by |
| 7 | + * translating the data into a radix-64 representation. The term Base64 |
| 8 | + * originates from a specific MIME content transfer encoding. Each non-final |
| 9 | + * Base64 digit represents exactly 6 bits of data. Three 8-bit bytes (i.e., a |
| 10 | + * total of 24 bits) can therefore be represented by four 6-bit Base64 |
| 11 | + * digits. |
| 12 | + * @author [Ashish Daulatabad](https://github.com/AshishYUO) |
| 13 | + */ |
| 14 | +#include <array> /// for `std::array` |
| 15 | +#include <cassert> /// for `assert` operations |
| 16 | +#include <iostream> /// for IO operations |
| 17 | + |
| 18 | +/** |
| 19 | + * @namespace ciphers |
| 20 | + * @brief Cipher algorithms |
| 21 | + */ |
| 22 | +namespace ciphers { |
| 23 | +/** |
| 24 | + * @namespace base64_encoding |
| 25 | + * @brief Functions for [Base64 Encoding and |
| 26 | + * Decoding](https://en.wikipedia.org/wiki/Base64) implementation. |
| 27 | + */ |
| 28 | +namespace base64_encoding { |
| 29 | +// chars denoting the format for encoding and decoding array. |
| 30 | +// This array is already decided by |
| 31 | +// [RFC4648](https://tools.ietf.org/html/rfc4648#section-4) standard |
| 32 | +const std::string chars = |
| 33 | + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| 34 | +/** |
| 35 | + * @brief Base64 Encoder |
| 36 | + * @details Converts the given string to Base64 equivalent. |
| 37 | + * @param input Input as a string |
| 38 | + * @returns Base64 encoded string |
| 39 | + */ |
| 40 | +std::string base64_encode(const std::string &input) { |
| 41 | + std::string base64_string; /// Output of this function: base64 string |
| 42 | + // base64 deals with 6-bit chars encoded as per chars, so |
| 43 | + // we will always filter 6-bits from input. |
| 44 | + for (uint32_t i = 0; i < input.size(); i += 3) { |
| 45 | + char first_byte = input[i]; /// First byte of the iteration |
| 46 | + // Take first six bits of first character. |
| 47 | + // Encode the first six bits with character defined in string `chars` |
| 48 | + base64_string.push_back(chars[first_byte >> 2]); |
| 49 | + |
| 50 | + if (i + 1 < input.size()) { |
| 51 | + char second_byte = input[i + 1]; /// Second byte of the iteration |
| 52 | + // Take remaining two bits of first character, and four first bits |
| 53 | + // from second character Combine two numbers as 6-bit digits and |
| 54 | + // encode by array chars (first two bits of first byte and next four |
| 55 | + // of second byte) |
| 56 | + base64_string.push_back( |
| 57 | + chars[(((first_byte & 3) << 4) | ((second_byte & 0xF0) >> 4))]); |
| 58 | + |
| 59 | + if (i + 2 < input.size()) { |
| 60 | + char third_byte = input[i + 2]; /// Third byte of the iteration |
| 61 | + // Take remaining four bits of second character, and first two |
| 62 | + // bits from third character Combine two numbers as 6-bit digits |
| 63 | + // and encode by array chars (remaining four bits of second byte |
| 64 | + // and first two of third byte) |
| 65 | + base64_string.push_back(chars[((third_byte & 0xC0) >> 6) | |
| 66 | + ((second_byte & 0x0F) << 2)]); |
| 67 | + // Encode remaining 6-bit of third byte by array chars |
| 68 | + base64_string.push_back(chars[(third_byte & 0x3F)]); |
| 69 | + } else { |
| 70 | + // Take remaining four bits of second character as 6-bit number |
| 71 | + base64_string.push_back(chars[((second_byte & 0x0F) << 2)]); |
| 72 | + base64_string.push_back('='); // padding characters |
| 73 | + } |
| 74 | + } else { |
| 75 | + // Take remaining two bits of first character as 6-bit number |
| 76 | + base64_string.push_back(chars[((first_byte & 3) << 4)]); |
| 77 | + base64_string.push_back('='); // padding characters |
| 78 | + base64_string.push_back('='); // padding characters |
| 79 | + } |
| 80 | + } |
| 81 | + return base64_string; |
| 82 | +} |
| 83 | +/** |
| 84 | + * @brief Utility function for finding index |
| 85 | + * @details Utility function for finding the position of a character in array |
| 86 | + * `chars` |
| 87 | + * @param c character to search in array `chars` |
| 88 | + * @returns integer denoting position of character in array `chars` |
| 89 | + */ |
| 90 | +uint8_t find_idx(const char c) { |
| 91 | + if (c >= 'A' && c <= 'Z') { |
| 92 | + return c - 'A'; |
| 93 | + } else if (c >= 'a' && c <= 'z') { |
| 94 | + return c - 'a' + 26; |
| 95 | + } else if (c >= '0' && c <= '9') { |
| 96 | + return c - '0' + 52; |
| 97 | + } else if (c == '+') { |
| 98 | + return 62; |
| 99 | + } else if (c == '/') { |
| 100 | + return 63; |
| 101 | + } |
| 102 | + return -1; |
| 103 | +} |
| 104 | +/** |
| 105 | + * @brief Base64 Decoder |
| 106 | + * @details Decodes the Base64 string |
| 107 | + * @param base64_str Input as a Base64 string |
| 108 | + * @returns Base64 decoded string |
| 109 | + */ |
| 110 | +std::string base64_decode(const std::string &base64_str) { |
| 111 | + std::string |
| 112 | + base64_decoded; /// Output of this function: base64 decoded string |
| 113 | + for (uint32_t i = 0; i < base64_str.size(); i += 4) { |
| 114 | + /// First 6-bit representation of Base64 |
| 115 | + char first_byte = base64_str[i]; |
| 116 | + /// Second 6-bit representation of Base64 |
| 117 | + char second_byte = base64_str[i + 1]; |
| 118 | + // Actual str characters are of 8 bits (or 1 byte): |
| 119 | + // :: 8 bits are decode by taking 6 bits from 1st byte of base64 string |
| 120 | + // and first 2 bits from 2nd byte of base64 string. |
| 121 | + char first_actual_byte = static_cast<char>( |
| 122 | + (find_idx(first_byte) << 2) | ((find_idx(second_byte)) >> 4)); |
| 123 | + base64_decoded.push_back(first_actual_byte); |
| 124 | + if (i + 2 < base64_str.size() && base64_str[i + 2] != '=') { |
| 125 | + /// Third 6-bit representation of Base64 |
| 126 | + char third_byte = base64_str[i + 2]; |
| 127 | + // :: Next 8 bits are decode by taking remaining 4 bits from 2nd |
| 128 | + // byte of base64 string and first 4 bits from 3rd byte of base64 |
| 129 | + // string. |
| 130 | + char second_actual_byte = |
| 131 | + static_cast<char>(((find_idx(second_byte) & 0x0F) << 4) | |
| 132 | + (find_idx(third_byte) >> 2)); |
| 133 | + base64_decoded.push_back(second_actual_byte); |
| 134 | + |
| 135 | + if (i + 3 < base64_str.size() && base64_str[i + 3] != '=') { |
| 136 | + /// Fourth 6-bit representation of Base64 string |
| 137 | + char fourth_byte = base64_str[i + 3]; |
| 138 | + // :: Taking remaining 2 bits from 3rd byte of base64 string |
| 139 | + // and all 6 bits from 4th byte of base64 string. |
| 140 | + char third_actual_byte = |
| 141 | + static_cast<char>(((find_idx(third_byte) & 0x03) << 6) | |
| 142 | + find_idx(fourth_byte)); |
| 143 | + base64_decoded.push_back(third_actual_byte); |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | + return base64_decoded; |
| 148 | +} |
| 149 | +} // namespace base64_encoding |
| 150 | +} // namespace ciphers |
| 151 | + |
| 152 | +/** |
| 153 | + * @brief Self test-implementations |
| 154 | + * @returns void |
| 155 | + */ |
| 156 | +static void test() { |
| 157 | + // 1st Test |
| 158 | + std::string str = |
| 159 | + "To err is human, but to really foul things up you need a computer."; |
| 160 | + std::string base64_str = ciphers::base64_encoding::base64_encode(str); |
| 161 | + std::string verify = |
| 162 | + "VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIHlvdSBuZW" |
| 163 | + "VkIGEgY29tcHV0ZXIu"; |
| 164 | + // verify encoding |
| 165 | + assert(base64_str == verify); |
| 166 | + std::string original_str = |
| 167 | + ciphers::base64_encoding::base64_decode(base64_str); |
| 168 | + // verify decoding |
| 169 | + assert(original_str == str); |
| 170 | + |
| 171 | + // 2nd Test from [Wikipedia](https://en.wikipedia.org/wiki/Base64) |
| 172 | + str = |
| 173 | + "Man is distinguished, not only by his reason, but by this singular " |
| 174 | + "passion from other animals, which is a lust of the mind, that by a " |
| 175 | + "perseverance of delight in the continued and indefatigable generation " |
| 176 | + "of knowledge, exceeds the short vehemence of any carnal pleasure."; |
| 177 | + |
| 178 | + base64_str = ciphers::base64_encoding::base64_encode(str); |
| 179 | + verify = |
| 180 | + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS" |
| 181 | + "B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBh" |
| 182 | + "IGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodC" |
| 183 | + "BpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25v" |
| 184 | + "d2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbG" |
| 185 | + "Vhc3VyZS4="; |
| 186 | + // verify encoding |
| 187 | + assert(base64_str == verify); |
| 188 | + original_str = ciphers::base64_encoding::base64_decode(base64_str); |
| 189 | + // verify decoding |
| 190 | + assert(original_str == str); |
| 191 | +} |
| 192 | + |
| 193 | +/** |
| 194 | + * @brief Main function |
| 195 | + * @returns 0 on exit |
| 196 | + */ |
| 197 | +int main() { |
| 198 | + test(); // run self-test implementations |
| 199 | + return 0; |
| 200 | +} |
0 commit comments