#include <stdexcept>
#include <algorithm>
#include <cmath> // for round()
+#include <climits>
#include <wchar.h>
#include <stdlib.h>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/foreach.hpp>
+#include <boost/assert.hpp>
+#include <boost/shared_ptr.hpp>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+
#include <stringfunc.hxx>
using namespace std;
return output;
}
+typedef boost::shared_ptr<BIO> BIO_Ptr;
+
+/**
+* @brief Converts openssl generic input/output to std::string
+*
+* Code adapted from keymakerd.
+*
+* @param bio Openssl's generic input/output
+* @return :string STL string
+**/
+static std::string _convert_BIO_to_string(BIO *input)
+{
+ std::string rtn;
+
+ char *output = NULL;
+ long written = BIO_get_mem_data(input, &output);
+ if (written <= 0 || output == NULL)
+ return rtn;
+
+ rtn.assign(output, written); //lint !e534 !e732
+ return rtn;
+} //lint !e1764
+
+/**
+ * @brief base64 encode a string using OpenSSL base64 functions
+ *
+ * Data size limit is 2GB on 32 bit (LONG_MAX)
+ *
+ * @param input String to encode
+ * @return base64 encoded string
+ */
+std::string base64_encode(const std::string &input)
+{
+ // check for empty buffer
+ if (input.empty())
+ return input;
+
+ // safety check to ensure our check afer BIO_write() works
+ if (input.size() >= LONG_MAX)
+ throw runtime_error("base64 encode: Too much data");
+
+ // setup encoder. Note: BIO_free_all frees both BIOs.
+ BIO_Ptr base64_encoder(BIO_new(BIO_f_base64()), BIO_free_all);
+ BIO *encoder_bio = base64_encoder.get();
+ BIO_set_flags(encoder_bio, BIO_FLAGS_BASE64_NO_NL);
+
+ // chain output buffer and encoder together
+ BIO *encoded_result = BIO_new(BIO_s_mem());
+ BIO_push(encoder_bio, encoded_result);
+
+ // encode
+ long written = BIO_write(encoder_bio, input.c_str(), input.size());
+ if ((unsigned)written != input.size())
+ {
+ ostringstream out;
+ out << "base64 encoding failed: input size: "
+ << input.size() << " vs. output size: " << written;
+ throw runtime_error(out.str());
+ }
+ if (BIO_flush(encoder_bio) != 1)
+ throw runtime_error("base64 encode: BIO_flush() failed");
+
+ return _convert_BIO_to_string(encoded_result);
+}
+
+/**
+ * @brief base64 decode a string using OpenSSL base64 functions
+ *
+ * @param input String to decode
+ * @return base64 decoded string
+ */
+std::string base64_decode(const std::string &input)
+{
+ // check for empty buffer
+ if (input.empty())
+ return input;
+
+ // safety check for BIO_new_mem_buf()
+ if (input.size() >= INT_MAX)
+ throw runtime_error("base64 decode: Too much data");
+
+ // setup encoder. Note: BIO_free_all frees both BIOs.
+ BIO_Ptr base64_decoder(BIO_new(BIO_f_base64()), BIO_free_all);
+ BIO *bio_base64 = base64_decoder.get();
+ BIO_set_flags(bio_base64, BIO_FLAGS_BASE64_NO_NL);
+
+ // chain input buffer and decoder together
+ BIO *bio_input = BIO_new_mem_buf((void*)input.c_str(), input.size());
+ bio_input = BIO_push(bio_base64, bio_input);
+
+ BIO_Ptr decoded_result(BIO_new(BIO_s_mem()), BIO_free_all);
+ BIO *bio_decoded = decoded_result.get();
+ const int convbuf_size = 512;
+ char convbuf[convbuf_size];
+
+ long read_bytes = 0;
+ while((read_bytes = BIO_read(bio_input, convbuf, convbuf_size)) > 0)
+ {
+ BOOST_ASSERT(read_bytes <= convbuf_size);
+ long written_bytes = BIO_write(bio_decoded, convbuf, read_bytes);
+ if (written_bytes != read_bytes)
+ {
+ ostringstream out;
+ out << "base64 decoding failed: read_bytes: "
+ << read_bytes << " vs. written_bytes: " << written_bytes;
+ throw runtime_error(out.str());
+ }
+ }
+ if (read_bytes == -2 || read_bytes == -1)
+ throw runtime_error("base64 decode: Error during decoding");
+
+ return _convert_BIO_to_string(bio_decoded);
+}
+
} // eo namespace I2n
BOOST_CHECK_EQUAL(shorten_stl_types(text), text);
}
+BOOST_AUTO_TEST_CASE(base64_encode_decode)
+{
+ string text = "Hello World\n";
+
+ string encoded = base64_encode(text);
+
+ BOOST_CHECK_EQUAL("SGVsbG8gV29ybGQK", encoded);
+ BOOST_CHECK_EQUAL(text, base64_decode(encoded));
+}
+
+BOOST_AUTO_TEST_CASE(base64_empty_string)
+{
+ string text = "";
+ string encoded = base64_encode(text);
+
+ BOOST_CHECK_EQUAL("", encoded);
+ BOOST_CHECK_EQUAL(text, base64_decode(encoded));
+}
+
+BOOST_AUTO_TEST_CASE(base64_large_string_with_zero)
+{
+ // 10 MB data
+ int data_size = 1024 * 1024 * 10;
+
+ string large_binary_data(data_size, 0);
+ BOOST_CHECK_EQUAL(data_size, large_binary_data.size());
+
+ string encoded = base64_encode(large_binary_data);
+
+ string decoded = base64_decode(encoded);
+ BOOST_CHECK_EQUAL(large_binary_data, decoded);
+}
+
+BOOST_AUTO_TEST_CASE(base64_decode_garbage)
+{
+ std::string data = "Hello World, this is unencoded data";
+ string decoded = base64_decode(data);
+
+ // garbage turns out to be an empty string
+ BOOST_CHECK_EQUAL(0, decoded.size());
+}
+
BOOST_AUTO_TEST_SUITE_END()