Commit c635b9ac authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

base64 encoder and decoder using boost base64_from_binary and

binary_from_base64


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/parkinglot@727 e5f2f494-b856-4b98-b285-d166d9295462
parent 06fb83af
......@@ -15,6 +15,7 @@ libdns_la_SOURCES += rdataclass.h rdataclass.cc
libdns_la_SOURCES += rrset.h rrset.cc
libdns_la_SOURCES += question.h question.cc
libdns_la_SOURCES += message.h message.cc
libdns_la_SOURCES += base64.h base64.cc
libdns_la_SOURCES += exceptions.h exceptions.cc
rrclass.h: rrclass-placeholder.h
......@@ -36,6 +37,7 @@ run_unittests_SOURCES += rrset_unittest.cc
run_unittests_SOURCES += question_unittest.cc
run_unittests_SOURCES += rrparamregistry_unittest.cc
run_unittests_SOURCES += message_unittest.cc
run_unittests_SOURCES += base64_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#include <cassert>
#include <iterator>
#include <string>
#include <vector>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include "base64.h"
#include "exceptions.h"
using namespace std;
using namespace boost::archive::iterators;
namespace isc {
namespace dns {
namespace {
const char BINARY_ZERO_CODE = 0;
typedef
class BinaryNormalizer : public iterator<input_iterator_tag, char> {
public:
BinaryNormalizer(const vector<char>::const_iterator& base,
const vector<char>::const_iterator& base_end) :
base_(base), base_end_(base_end), in_pad_(false)
{}
BinaryNormalizer& operator++()
{
if (!in_pad_) {
++base_;
}
if (base_ == base_end_) {
in_pad_ = true;
}
return (*this);
}
const char& operator*() const {
if (in_pad_) {
return (BINARY_ZERO_CODE);
} else {
return (*base_);
}
}
bool operator==(const BinaryNormalizer& other) const
{
return (base_ == other.base_);
}
private:
vector<char>::const_iterator base_;
const vector<char>::const_iterator base_end_;
bool in_pad_;
};
typedef
base64_from_binary<transform_width<BinaryNormalizer, 6, 8> > base64_encoder;
} // end of anonymous namespace
string
encodeBase64(const vector<char>& binary)
{
// calculate the resulting length. it's the smallest multiple of 4
// equal to or larger than 4/3 * original data length.
size_t len = ((binary.size() * 4 / 3) + 3) & ~3;
string base64;
base64.reserve(len);
base64.assign(base64_encoder(BinaryNormalizer(binary.begin(),
binary.end())),
base64_encoder(BinaryNormalizer(binary.end(), binary.end())));
assert(len >= base64.length());
base64.append(len - base64.length(), '=');
return (base64);
}
namespace {
const char BASE64_PADDING_CHAR = '=';
const size_t BASE64_MAX_PADDING_CHARS = 2;
const char BASE64_ZERO_CODE = 'A'; // correspond to 000000(2)
class Base64Normalizer : public iterator<input_iterator_tag, char> {
public:
Base64Normalizer(const string::const_iterator& base,
const string::const_iterator& base_beginpad,
const string::const_iterator& base_end) :
base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
in_pad_(false)
{}
Base64Normalizer& operator++()
{
++base_;
while (base_ != base_end_ && isspace(*base_)) {
++base_;
}
if (base_ == base_beginpad_) {
in_pad_ = true;
}
return (*this);
}
const char operator*() const {
if (in_pad_ && *base_ == BASE64_PADDING_CHAR) {
return (BASE64_ZERO_CODE);
} else {
return (*base_);
}
}
bool operator==(const Base64Normalizer& other) const
{
return (base_ == other.base_);
}
private:
string::const_iterator base_;
const string::const_iterator base_beginpad_;
const string::const_iterator base_end_;
bool in_pad_;
};
typedef
transform_width<binary_from_base64<Base64Normalizer, char>, 8, 6, char>
base64_decoder;
} // end of anonymous namespace
void
decodeBase64(const string& base64, vector<char>& result)
{
// enumerate the number of trailing padding characters (=), ignoring
// white spaces. since base64_from_binary doesn't accept padding,
// we handle it explicitly.
size_t padlen = 0;
string::const_reverse_iterator srit = base64.rbegin();
string::const_reverse_iterator srit_end = base64.rend();
while (srit != srit_end) {
char ch = *srit;
if (ch == BASE64_PADDING_CHAR) {
if (++padlen > BASE64_MAX_PADDING_CHARS) {
dns_throw(BadBase64String,
"Too many Base64 padding characters");
}
} else if (!isspace(ch)) {
break;
}
++srit;
}
try {
result.assign(base64_decoder(Base64Normalizer(base64.begin(),
srit.base(),
base64.end())),
base64_decoder(Base64Normalizer(base64.end(),
base64.end(),
base64.end())));
} catch (dataflow_exception& ex) {
dns_throw(BadBase64String, ex.what());
}
// Confirm the original base64 text is the canonical encoding of the
// data.
assert(result.size() >= padlen);
vector<char>::const_reverse_iterator rit = result.rbegin();
for (int i = 0; i < padlen; ++i, ++rit) {
if (*rit != 0) {
dns_throw(BadBase64String, "Non 0 bits included in padding");
}
}
// strip the padded zero-bit fields
result.resize(result.size() - padlen);
}
}
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#ifndef __BASE64_H
#define __BASE64_H 1
#include <string>
#include <vector>
#include "exceptions.h"
//
// Note: this helper module isn't specific to the DNS protocol per se.
// We should probably move this to somewhere else, possibly in some common
// utility area.
//
namespace isc {
namespace dns {
///
/// \brief A standard DNS (or ISC) module exception that is thrown a Base64
/// decoder encounters an invalid input.
///
class BadBase64String : public Exception {
public:
BadBase64String(const char* file, size_t line, const char* what) :
isc::dns::Exception(file, line, what) {}
};
std::string encodeBase64(const std::vector<char>& binary);
void decodeBase64(const std::string& base64, std::vector<char>& result);
}
}
#endif // __BASE64_H
// Local Variables:
// mode: c++
// End:
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id$
#include <string>
#include <utility>
#include <vector>
#include "base64.h"
#include <gtest/gtest.h>
using namespace std;
using namespace isc::dns;
namespace {
typedef pair<string, string> StringPair;
class Base64Test : public ::testing::Test {
protected:
Base64Test()
{
// test vectors from RFC4648
test_sequence.push_back(StringPair("", ""));
test_sequence.push_back(StringPair("f", "Zg=="));
test_sequence.push_back(StringPair("fo", "Zm8="));
test_sequence.push_back(StringPair("foo", "Zm9v"));
test_sequence.push_back(StringPair("foob", "Zm9vYg=="));
test_sequence.push_back(StringPair("fooba", "Zm9vYmE="));
test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
}
vector<StringPair> test_sequence;
vector<char> decoded_data;
};
void
decodeCheck(const string& input_string, vector<char>& output,
const string& expected)
{
decodeBase64(input_string, output);
EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
}
TEST_F(Base64Test, decode)
{
for (vector<StringPair>::const_iterator it = test_sequence.begin();
it != test_sequence.end();
++it) {
decodeCheck((*it).second, decoded_data, (*it).first);
}
// whitespace should be allowed
decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
decodeCheck("Zm9vYg==", decoded_data, "foob");
decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
// only up to 2 padding characters are allowed
EXPECT_THROW(decodeBase64("A===", decoded_data), BadBase64String);
EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadBase64String);
// intermediate padding isn't allowed
EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadBase64String);
// Non canonical form isn't allowed.
// Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
// byte would be 0100 0000.
EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadBase64String);
// Same for the 1st padding byte. This would make it 01100000.
EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadBase64String);
}
TEST_F(Base64Test, encode)
{
for (vector<StringPair>::const_iterator it = test_sequence.begin();
it != test_sequence.end();
++it) {
decoded_data.assign((*it).first.begin(), (*it).first.end());
EXPECT_EQ((*it).second, encodeBase64(decoded_data));
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment