base64.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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 "config.h"
  16. #include <stdint.h>
  17. #include <cassert>
  18. #include <iterator>
  19. #include <string>
  20. #include <vector>
  21. #include <boost/archive/iterators/base64_from_binary.hpp>
  22. #include <boost/archive/iterators/binary_from_base64.hpp>
  23. #include <boost/archive/iterators/transform_width.hpp>
  24. #include "base64.h"
  25. #include <exceptions/exceptions.h>
  26. using namespace std;
  27. using namespace boost::archive::iterators;
  28. namespace isc {
  29. namespace dns {
  30. namespace {
  31. const char BASE64_PADDING_CHAR = '=';
  32. const uint8_t BINARY_ZERO_CODE = 0;
  33. class BinaryNormalizer : public iterator<input_iterator_tag, uint8_t> {
  34. public:
  35. BinaryNormalizer(const vector<uint8_t>::const_iterator& base,
  36. const vector<uint8_t>::const_iterator& base_end) :
  37. base_(base), base_end_(base_end), in_pad_(false)
  38. {}
  39. BinaryNormalizer& operator++()
  40. {
  41. if (!in_pad_) {
  42. ++base_;
  43. }
  44. if (base_ == base_end_) {
  45. in_pad_ = true;
  46. }
  47. return (*this);
  48. }
  49. const uint8_t& operator*() const {
  50. if (in_pad_) {
  51. return (BINARY_ZERO_CODE);
  52. } else {
  53. return (*base_);
  54. }
  55. }
  56. bool operator==(const BinaryNormalizer& other) const
  57. {
  58. return (base_ == other.base_);
  59. }
  60. private:
  61. vector<uint8_t>::const_iterator base_;
  62. const vector<uint8_t>::const_iterator base_end_;
  63. bool in_pad_;
  64. };
  65. typedef
  66. base64_from_binary<transform_width<BinaryNormalizer, 6, 8> > base64_encoder;
  67. } // end of anonymous namespace
  68. string
  69. encodeBase64(const vector<uint8_t>& binary)
  70. {
  71. // calculate the resulting length. it's the smallest multiple of 4
  72. // equal to or larger than 4/3 * original data length.
  73. size_t len = ((binary.size() * 4 / 3) + 3) & ~3;
  74. string base64;
  75. base64.reserve(len);
  76. base64.assign(base64_encoder(BinaryNormalizer(binary.begin(),
  77. binary.end())),
  78. base64_encoder(BinaryNormalizer(binary.end(), binary.end())));
  79. assert(len >= base64.length());
  80. base64.append(len - base64.length(), BASE64_PADDING_CHAR);
  81. return (base64);
  82. }
  83. namespace {
  84. const size_t BASE64_MAX_PADDING_CHARS = 2;
  85. const char BASE64_ZERO_CODE = 'A'; // correspond to 000000(2)
  86. class Base64Normalizer : public iterator<input_iterator_tag, char> {
  87. public:
  88. Base64Normalizer(const string::const_iterator& base,
  89. const string::const_iterator& base_beginpad,
  90. const string::const_iterator& base_end) :
  91. base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
  92. in_pad_(false)
  93. {}
  94. Base64Normalizer& operator++()
  95. {
  96. ++base_;
  97. while (base_ != base_end_ && isspace(*base_)) {
  98. ++base_;
  99. }
  100. if (base_ == base_beginpad_) {
  101. in_pad_ = true;
  102. }
  103. return (*this);
  104. }
  105. const char& operator*() const {
  106. if (in_pad_ && *base_ == BASE64_PADDING_CHAR) {
  107. return (BASE64_ZERO_CODE);
  108. } else {
  109. return (*base_);
  110. }
  111. }
  112. bool operator==(const Base64Normalizer& other) const
  113. {
  114. return (base_ == other.base_);
  115. }
  116. private:
  117. string::const_iterator base_;
  118. const string::const_iterator base_beginpad_;
  119. const string::const_iterator base_end_;
  120. bool in_pad_;
  121. };
  122. typedef
  123. transform_width<binary_from_base64<Base64Normalizer, char>, 8, 6, char>
  124. base64_decoder;
  125. } // end of anonymous namespace
  126. void
  127. decodeBase64(const string& base64, vector<uint8_t>& result)
  128. {
  129. // enumerate the number of trailing padding characters (=), ignoring
  130. // white spaces. since base64_from_binary doesn't accept padding,
  131. // we handle it explicitly.
  132. size_t padlen = 0;
  133. string::const_reverse_iterator srit = base64.rbegin();
  134. string::const_reverse_iterator srit_end = base64.rend();
  135. while (srit != srit_end) {
  136. char ch = *srit;
  137. if (ch == BASE64_PADDING_CHAR) {
  138. if (++padlen > BASE64_MAX_PADDING_CHARS) {
  139. isc_throw(BadBase64String,
  140. "Too many Base64 padding characters");
  141. }
  142. } else if (!isspace(ch)) {
  143. break;
  144. }
  145. ++srit;
  146. }
  147. try {
  148. result.assign(base64_decoder(Base64Normalizer(base64.begin(),
  149. srit.base(),
  150. base64.end())),
  151. base64_decoder(Base64Normalizer(base64.end(),
  152. base64.end(),
  153. base64.end())));
  154. } catch (dataflow_exception& ex) {
  155. isc_throw(BadBase64String, ex.what());
  156. }
  157. // Confirm the original base64 text is the canonical encoding of the
  158. // data.
  159. assert(result.size() >= padlen);
  160. vector<uint8_t>::const_reverse_iterator rit = result.rbegin();
  161. for (int i = 0; i < padlen; ++i, ++rit) {
  162. if (*rit != 0) {
  163. isc_throw(BadBase64String, "Non 0 bits included in padding");
  164. }
  165. }
  166. // strip the padded zero-bit fields
  167. result.resize(result.size() - padlen);
  168. }
  169. }
  170. }