base64.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. // $Id$
  15. #include <stdint.h>
  16. #include <cassert>
  17. #include <iterator>
  18. #include <string>
  19. #include <vector>
  20. #include <boost/archive/iterators/base64_from_binary.hpp>
  21. #include <boost/archive/iterators/binary_from_base64.hpp>
  22. #include <boost/archive/iterators/transform_width.hpp>
  23. #include <exceptions/exceptions.h>
  24. #include <dns/base64.h>
  25. using namespace std;
  26. using namespace boost::archive::iterators;
  27. namespace isc {
  28. namespace dns {
  29. namespace {
  30. const char BASE64_PADDING_CHAR = '=';
  31. const uint8_t BINARY_ZERO_CODE = 0;
  32. class BinaryNormalizer : public iterator<input_iterator_tag, uint8_t> {
  33. public:
  34. BinaryNormalizer(const vector<uint8_t>::const_iterator& base,
  35. const vector<uint8_t>::const_iterator& base_end) :
  36. base_(base), base_end_(base_end), in_pad_(false)
  37. {}
  38. BinaryNormalizer& operator++()
  39. {
  40. if (!in_pad_) {
  41. ++base_;
  42. }
  43. if (base_ == base_end_) {
  44. in_pad_ = true;
  45. }
  46. return (*this);
  47. }
  48. const uint8_t& operator*() const {
  49. if (in_pad_) {
  50. return (BINARY_ZERO_CODE);
  51. } else {
  52. return (*base_);
  53. }
  54. }
  55. bool operator==(const BinaryNormalizer& other) const
  56. {
  57. return (base_ == other.base_);
  58. }
  59. private:
  60. vector<uint8_t>::const_iterator base_;
  61. const vector<uint8_t>::const_iterator base_end_;
  62. bool in_pad_;
  63. };
  64. typedef
  65. base64_from_binary<transform_width<BinaryNormalizer, 6, 8> > base64_encoder;
  66. } // end of anonymous namespace
  67. string
  68. encodeBase64(const vector<uint8_t>& binary)
  69. {
  70. // calculate the resulting length. it's the smallest multiple of 4
  71. // equal to or larger than 4/3 * original data length.
  72. size_t len = ((binary.size() * 4 / 3) + 3) & ~3;
  73. string base64;
  74. base64.reserve(len);
  75. base64.assign(base64_encoder(BinaryNormalizer(binary.begin(),
  76. binary.end())),
  77. base64_encoder(BinaryNormalizer(binary.end(), binary.end())));
  78. assert(len >= base64.length());
  79. base64.append(len - base64.length(), BASE64_PADDING_CHAR);
  80. return (base64);
  81. }
  82. namespace {
  83. const size_t BASE64_MAX_PADDING_CHARS = 2;
  84. const char BASE64_ZERO_CODE = 'A'; // correspond to 000000(2)
  85. class Base64Normalizer : public iterator<input_iterator_tag, char> {
  86. public:
  87. Base64Normalizer(const string::const_iterator& base,
  88. const string::const_iterator& base_beginpad,
  89. const string::const_iterator& base_end) :
  90. base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
  91. in_pad_(false)
  92. {}
  93. Base64Normalizer& operator++()
  94. {
  95. ++base_;
  96. while (base_ != base_end_ && isspace(*base_)) {
  97. ++base_;
  98. }
  99. if (base_ == base_beginpad_) {
  100. in_pad_ = true;
  101. }
  102. return (*this);
  103. }
  104. const char& operator*() const {
  105. if (in_pad_ && *base_ == BASE64_PADDING_CHAR) {
  106. return (BASE64_ZERO_CODE);
  107. } else {
  108. return (*base_);
  109. }
  110. }
  111. bool operator==(const Base64Normalizer& other) const
  112. {
  113. return (base_ == other.base_);
  114. }
  115. private:
  116. string::const_iterator base_;
  117. const string::const_iterator base_beginpad_;
  118. const string::const_iterator base_end_;
  119. bool in_pad_;
  120. };
  121. typedef
  122. transform_width<binary_from_base64<Base64Normalizer, char>, 8, 6, char>
  123. base64_decoder;
  124. } // end of anonymous namespace
  125. void
  126. decodeBase64(const string& base64, vector<uint8_t>& result)
  127. {
  128. // enumerate the number of trailing padding characters (=), ignoring
  129. // white spaces. since base64_from_binary doesn't accept padding,
  130. // we handle it explicitly.
  131. size_t padlen = 0;
  132. string::const_reverse_iterator srit = base64.rbegin();
  133. string::const_reverse_iterator srit_end = base64.rend();
  134. while (srit != srit_end) {
  135. char ch = *srit;
  136. if (ch == BASE64_PADDING_CHAR) {
  137. if (++padlen > BASE64_MAX_PADDING_CHARS) {
  138. isc_throw(BadBase64String,
  139. "Too many Base64 padding characters");
  140. }
  141. } else if (!isspace(ch)) {
  142. break;
  143. }
  144. ++srit;
  145. }
  146. try {
  147. result.assign(base64_decoder(Base64Normalizer(base64.begin(),
  148. srit.base(),
  149. base64.end())),
  150. base64_decoder(Base64Normalizer(base64.end(),
  151. base64.end(),
  152. base64.end())));
  153. } catch (dataflow_exception& ex) {
  154. isc_throw(BadBase64String, ex.what());
  155. }
  156. // Confirm the original base64 text is the canonical encoding of the
  157. // data.
  158. assert(result.size() >= padlen);
  159. vector<uint8_t>::const_reverse_iterator rit = result.rbegin();
  160. for (int i = 0; i < padlen; ++i, ++rit) {
  161. if (*rit != 0) {
  162. isc_throw(BadBase64String, "Non 0 bits included in padding");
  163. }
  164. }
  165. // strip the padded zero-bit fields
  166. result.resize(result.size() - padlen);
  167. }
  168. }
  169. }