Commit 9a6b7baf authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1575] Introduced a new NSEC3Hash class based on a Nsec3Param in datasrc,

fixing bugs like case-sensitivity or corner cases for empty salts.
provided more detailed tests and documentation.
parent 745181fc
......@@ -93,6 +93,7 @@ libdns___la_SOURCES += masterload.h masterload.cc
libdns___la_SOURCES += message.h message.cc
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
libdns___la_SOURCES += name.h name.cc
libdns___la_SOURCES += nsec3hash.h nsec3hash.cc
libdns___la_SOURCES += opcode.h opcode.cc
libdns___la_SOURCES += rcode.h rcode.cc
libdns___la_SOURCES += rdata.h rdata.cc
......
// Copyright (C) 2012 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.
#include <stdint.h>
#include <cassert>
#include <string>
#include <vector>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <util/encode/base32hex.h>
#include <util/hash/sha1.h>
#include <dns/name.h>
#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
using namespace std;
using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::util::hash;
using namespace isc::dns::rdata;
namespace {
// Currently the only pre-defined algorithm is SHA1. So we don't
// over-generalize it at the moment, and rather hardocde it and
// assume that specific algorithm.
const uint8_t NSEC3_HASH_SHA1 = 1;
}
namespace isc {
namespace dns {
struct NSEC3Hash::NSEC3HashImpl {
NSEC3HashImpl(const generic::NSEC3PARAM& param) :
algorithm_(param.getHashalg()),
iterations_(param.getIterations()),
salt_(param.getSalt()), digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
{
if (algorithm_ != NSEC3_HASH_SHA1) {
isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " <<
static_cast<unsigned int>(algorithm_));
}
SHA1Reset(&sha1_ctx_);
}
const uint8_t algorithm_;
const uint16_t iterations_;
const vector<uint8_t> salt_;
// The following members are placeholder of work place and don't hold
// any state over multiple calls so can be mutable without breaking
// constness.
mutable SHA1Context sha1_ctx_;
mutable vector<uint8_t> digest_;
mutable OutputBuffer obuf_;
};
NSEC3Hash::NSEC3Hash(const generic::NSEC3PARAM& param) :
impl_(new NSEC3HashImpl(param))
{}
NSEC3Hash::~NSEC3Hash() {
delete impl_;
}
namespace {
inline void
iterateSHA1(SHA1Context* ctx, const uint8_t* input, size_t inlength,
const uint8_t* salt, size_t saltlen,
uint8_t output[SHA1_HASHSIZE])
{
SHA1Reset(ctx);
SHA1Input(ctx, input, inlength);
SHA1Input(ctx, salt, saltlen); // this works whether saltlen == or > 0
SHA1Result(ctx, output);
}
}
string
NSEC3Hash::calculate(const Name& name) const {
// We first need to normalize the name by converting all upper case
// characters in the labels to lower ones.
impl_->obuf_.clear();
Name name_copy(name);
name_copy.downcase();
name_copy.toWire(impl_->obuf_);
const uint8_t saltlen = impl_->salt_.size();
const uint8_t* const salt = (saltlen > 0) ? &impl_->salt_[0] : NULL;
uint8_t* const digest = &impl_->digest_[0];
assert(impl_->digest_.size() == SHA1_HASHSIZE);
iterateSHA1(&impl_->sha1_ctx_,
static_cast<const uint8_t*>(impl_->obuf_.getData()),
impl_->obuf_.getLength(), salt, saltlen, digest);
for (unsigned int n = 0; n < impl_->iterations_; ++n) {
iterateSHA1(&impl_->sha1_ctx_, digest, SHA1_HASHSIZE,
salt, saltlen, digest);
}
return (encodeBase32Hex(impl_->digest_));
}
} // namespace dns
} // namespace isc
// Copyright (C) 2012 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.
#ifndef __NSEC3HASH_H
#define __NSEC3HASH_H 1
#include <string>
#include <boost/noncopyable.hpp>
#include <exceptions/exceptions.h>
namespace isc {
namespace dns {
class Name;
namespace rdata {
namespace generic {
class NSEC3PARAM;
}
}
/// An exception that is thrown for when an \c NSEC3Hash object is constructed
/// with an unknown hash algorithm.
///
/// A specific exception class is used so that the caller can selectively
/// catch this exception, e.g., while loading a zone, and handle it
/// accordingly.
class UnknownNSEC3HashAlgorithm : public isc::Exception {
public:
UnknownNSEC3HashAlgorithm(const char* file, size_t line,
const char* what) :
isc::Exception(file, line, what) {}
};
/// A calculator of NSEC3 hashes.
///
/// This is a simple class that encapsulates the algorithm of calculating
/// NSEC3 hash values as defined in RFC5155.
///
/// This class is designed to be "stateless" in that it basically doesn't
/// hold mutable state once constructed, and hash calculation solely depends
/// on the parameters given on construction and input to the \c calculate()
/// method. In that sense this could be a single free function rather than
/// a class, but we decided to provide the functionality as a class for
/// two reasons: NSEC3 hash calculations would often take place more than one
/// time in a single query or validation process, so it would be more
/// efficient if we could hold some internal resources used for the
/// calculation and reuse it over multiple calls to \c calculate() (this
/// implementation actually does this); Second, for testing purposes we may
/// want to use a fake calculator that returns pre-defined hash values
/// (so a slight change to the test input wouldn't affect the test result).
/// Using a class would make it possible by introducing a common base class
/// and having the application depend on that base class (then the fake
/// calculator will be defined as a separate subclass of the base).
///
/// The initial implementation makes this class non copyable as it wouldn't
/// used be passed from one place to another, especially if and when it's
/// used via a base class abstraction. But there's no fundamental reason
/// this cannot be copied, so if we see a specific need for it, this
/// restriction can be revisited.
///
/// There can be several ways to extend this class in future. Those include:
/// - Introduce a base class and make it derived from it (mainly for testing
/// purposes as noted above)
/// - Allow to construct the class from a tuple of parameters, that is,
/// integers for algorithm, iterations and flags, and opaque salt data.
/// For example, we might want to use that version for validators.
/// - Allow producing hash value as binary data
/// - Allow updating NSEC3 parameters of a class object so we can still reuse
/// the internal resources for different sets of parameters.
class NSEC3Hash : public boost::noncopyable {
public:
/// Constructor from NSEC3PARAM RDATA.
///
/// The hash algorithm given via \c param must be known to the
/// implementation. Otherwise \c UnknownNSEC3HashAlgorithm exception
/// will be thrown.
///
/// \throw UnknownNSEC3HashAlgorithm The specified algorithm in \c param
/// is unknown.
/// \throw std::bad_alloc Internal resource allocation failure.
///
/// \param param NSEC3 parameters used for subsequent calculation.
NSEC3Hash(const rdata::generic::NSEC3PARAM& param);
/// The destructor.
~NSEC3Hash();
/// Calculate the NSEC3 hash.
///
/// This method calculates the NSEC3 hash value for the given \c name
/// with the hash parameters (algorithm, iterations and salt) given at
/// construction, and returns the value in a base32hex-encoded string
/// (without containing any white spaces). All alphabets in the string
/// will be upper cased.
///
/// \param name The domain name for which the hash value is to be
/// calculated.
/// \return Base32hex-encoded string of the hash value.
std::string calculate(const Name& name) const;
private:
struct NSEC3HashImpl;
NSEC3HashImpl* impl_;
};
}
}
#endif // __NSEC3HASH_H
// Local Variables:
// mode: c++
// End:
......@@ -21,6 +21,7 @@ run_unittests_SOURCES = unittest_util.h unittest_util.cc
run_unittests_SOURCES += edns_unittest.cc
run_unittests_SOURCES += messagerenderer_unittest.cc
run_unittests_SOURCES += name_unittest.cc
run_unittests_SOURCES += nsec3hash_unittest.cc
run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
run_unittests_SOURCES += rrttl_unittest.cc
run_unittests_SOURCES += opcode_unittest.cc
......
// Copyright (C) 2012 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.
#include <gtest/gtest.h>
#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
using namespace isc::dns;
using namespace isc::dns::rdata;
namespace {
class NSEC3HashTest : public ::testing::Test {
protected:
NSEC3HashTest() : test_hash(generic::NSEC3PARAM("1 0 12 aabbccdd"))
{}
// An NSEC3Hash object commonly used in tests. Parameters are borrowed
// from the RFC5155 example. Construction of this object implicitly
// checks a successful case of the constructor.
const NSEC3Hash test_hash;
};
TEST_F(NSEC3HashTest, unknownAlgorithm) {
EXPECT_THROW(NSEC3Hash(generic::NSEC3PARAM("2 0 12 aabbccdd")),
UnknownNSEC3HashAlgorithm);
}
TEST_F(NSEC3HashTest, calculate) {
// A couple of normal cases from the RFC5155 example.
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash.calculate(Name("example")));
EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL",
test_hash.calculate(Name("a.example")));
// Check case-insensitiveness
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash.calculate(Name("EXAMPLE")));
// Some boundary cases: 0-iteration and empty salt. Borrowed from the
// .com zone data.
EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM",
NSEC3Hash(generic::NSEC3PARAM("1 0 0 -")).
calculate(Name("com")));
// Using unusually large iterations, something larger than the 8-bit range.
// (expected hash value generated by BIND 9's dnssec-signzone)
EXPECT_EQ("COG6A52MJ96MNMV3QUCAGGCO0RHCC2Q3",
NSEC3Hash(generic::NSEC3PARAM("1 0 256 AABBCCDD")).
calculate(Name("example.org")));
}
} // end namespace
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