From 0a596dd7aaa4ec1a17c6cd40f78cfc94fc7e47fc Mon Sep 17 00:00:00 2001 From: Ashish Bhanu Daulatabad Date: Wed, 31 Mar 2021 10:46:54 +0530 Subject: [PATCH] feat: Base64 Encoding/Decoding, type: Ciphers (#1471) * updating DIRECTORY.md * Feat: Base64 Encoding Decoding, Type: cipher * Feat: Base64 Encoding Decoding, Type: cipher-2 * Type checks * updating DIRECTORY.md * Wrong param explaination * Namespace and comments * Update ciphers/base64_encoding.cpp Co-authored-by: David Leal * Update ciphers/base64_encoding.cpp Co-authored-by: David Leal Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal --- DIRECTORY.md | 1 + ciphers/base64_encoding.cpp | 200 ++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 ciphers/base64_encoding.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index eded4504e..66683cfcd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -14,6 +14,7 @@ * [Hamming Distance](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/bit_manipulation/hamming_distance.cpp) ## Ciphers + * [Base64 Encoding](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/base64_encoding.cpp) * [Caesar Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/caesar_cipher.cpp) * [Hill Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/hill_cipher.cpp) * [Morse Code](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/morse_code.cpp) diff --git a/ciphers/base64_encoding.cpp b/ciphers/base64_encoding.cpp new file mode 100644 index 000000000..a592226a5 --- /dev/null +++ b/ciphers/base64_encoding.cpp @@ -0,0 +1,200 @@ +/** + * @brief [Base64 Encoding and + * Decoding](https://en.wikipedia.org/wiki/Base64) + * @details In programming, [Base64](https://en.wikipedia.org/wiki/Base64) is a + * group of binary-to-text encoding schemes that represent binary data (more + * specifically, a sequence of 8-bit bytes) in an ASCII string format by + * translating the data into a radix-64 representation. The term Base64 + * originates from a specific MIME content transfer encoding. Each non-final + * Base64 digit represents exactly 6 bits of data. Three 8-bit bytes (i.e., a + * total of 24 bits) can therefore be represented by four 6-bit Base64 + * digits. + * @author [Ashish Daulatabad](https://github.com/AshishYUO) + */ +#include /// for `std::array` +#include /// for `assert` operations +#include /// for IO operations + +/** + * @namespace ciphers + * @brief Cipher algorithms + */ +namespace ciphers { +/** + * @namespace base64_encoding + * @brief Functions for [Base64 Encoding and + * Decoding](https://en.wikipedia.org/wiki/Base64) implementation. + */ +namespace base64_encoding { +// chars denoting the format for encoding and decoding array. +// This array is already decided by +// [RFC4648](https://tools.ietf.org/html/rfc4648#section-4) standard +const std::string chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +/** + * @brief Base64 Encoder + * @details Converts the given string to Base64 equivalent. + * @param input Input as a string + * @returns Base64 encoded string + */ +std::string base64_encode(const std::string &input) { + std::string base64_string; /// Output of this function: base64 string + // base64 deals with 6-bit chars encoded as per chars, so + // we will always filter 6-bits from input. + for (uint32_t i = 0; i < input.size(); i += 3) { + char first_byte = input[i]; /// First byte of the iteration + // Take first six bits of first character. + // Encode the first six bits with character defined in string `chars` + base64_string.push_back(chars[first_byte >> 2]); + + if (i + 1 < input.size()) { + char second_byte = input[i + 1]; /// Second byte of the iteration + // Take remaining two bits of first character, and four first bits + // from second character Combine two numbers as 6-bit digits and + // encode by array chars (first two bits of first byte and next four + // of second byte) + base64_string.push_back( + chars[(((first_byte & 3) << 4) | ((second_byte & 0xF0) >> 4))]); + + if (i + 2 < input.size()) { + char third_byte = input[i + 2]; /// Third byte of the iteration + // Take remaining four bits of second character, and first two + // bits from third character Combine two numbers as 6-bit digits + // and encode by array chars (remaining four bits of second byte + // and first two of third byte) + base64_string.push_back(chars[((third_byte & 0xC0) >> 6) | + ((second_byte & 0x0F) << 2)]); + // Encode remaining 6-bit of third byte by array chars + base64_string.push_back(chars[(third_byte & 0x3F)]); + } else { + // Take remaining four bits of second character as 6-bit number + base64_string.push_back(chars[((second_byte & 0x0F) << 2)]); + base64_string.push_back('='); // padding characters + } + } else { + // Take remaining two bits of first character as 6-bit number + base64_string.push_back(chars[((first_byte & 3) << 4)]); + base64_string.push_back('='); // padding characters + base64_string.push_back('='); // padding characters + } + } + return base64_string; +} +/** + * @brief Utility function for finding index + * @details Utility function for finding the position of a character in array + * `chars` + * @param c character to search in array `chars` + * @returns integer denoting position of character in array `chars` + */ +uint8_t find_idx(const char c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A'; + } else if (c >= 'a' && c <= 'z') { + return c - 'a' + 26; + } else if (c >= '0' && c <= '9') { + return c - '0' + 52; + } else if (c == '+') { + return 62; + } else if (c == '/') { + return 63; + } + return -1; +} +/** + * @brief Base64 Decoder + * @details Decodes the Base64 string + * @param base64_str Input as a Base64 string + * @returns Base64 decoded string + */ +std::string base64_decode(const std::string &base64_str) { + std::string + base64_decoded; /// Output of this function: base64 decoded string + for (uint32_t i = 0; i < base64_str.size(); i += 4) { + /// First 6-bit representation of Base64 + char first_byte = base64_str[i]; + /// Second 6-bit representation of Base64 + char second_byte = base64_str[i + 1]; + // Actual str characters are of 8 bits (or 1 byte): + // :: 8 bits are decode by taking 6 bits from 1st byte of base64 string + // and first 2 bits from 2nd byte of base64 string. + char first_actual_byte = static_cast( + (find_idx(first_byte) << 2) | ((find_idx(second_byte)) >> 4)); + base64_decoded.push_back(first_actual_byte); + if (i + 2 < base64_str.size() && base64_str[i + 2] != '=') { + /// Third 6-bit representation of Base64 + char third_byte = base64_str[i + 2]; + // :: Next 8 bits are decode by taking remaining 4 bits from 2nd + // byte of base64 string and first 4 bits from 3rd byte of base64 + // string. + char second_actual_byte = + static_cast(((find_idx(second_byte) & 0x0F) << 4) | + (find_idx(third_byte) >> 2)); + base64_decoded.push_back(second_actual_byte); + + if (i + 3 < base64_str.size() && base64_str[i + 3] != '=') { + /// Fourth 6-bit representation of Base64 string + char fourth_byte = base64_str[i + 3]; + // :: Taking remaining 2 bits from 3rd byte of base64 string + // and all 6 bits from 4th byte of base64 string. + char third_actual_byte = + static_cast(((find_idx(third_byte) & 0x03) << 6) | + find_idx(fourth_byte)); + base64_decoded.push_back(third_actual_byte); + } + } + } + return base64_decoded; +} +} // namespace base64_encoding +} // namespace ciphers + +/** + * @brief Self test-implementations + * @returns void + */ +static void test() { + // 1st Test + std::string str = + "To err is human, but to really foul things up you need a computer."; + std::string base64_str = ciphers::base64_encoding::base64_encode(str); + std::string verify = + "VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIHlvdSBuZW" + "VkIGEgY29tcHV0ZXIu"; + // verify encoding + assert(base64_str == verify); + std::string original_str = + ciphers::base64_encoding::base64_decode(base64_str); + // verify decoding + assert(original_str == str); + + // 2nd Test from [Wikipedia](https://en.wikipedia.org/wiki/Base64) + str = + "Man is distinguished, not only by his reason, but by this singular " + "passion from other animals, which is a lust of the mind, that by a " + "perseverance of delight in the continued and indefatigable generation " + "of knowledge, exceeds the short vehemence of any carnal pleasure."; + + base64_str = ciphers::base64_encoding::base64_encode(str); + verify = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS" + "B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBh" + "IGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodC" + "BpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25v" + "d2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbG" + "Vhc3VyZS4="; + // verify encoding + assert(base64_str == verify); + original_str = ciphers::base64_encoding::base64_decode(base64_str); + // verify decoding + assert(original_str == str); +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +}