diff options
Diffstat (limited to 'src/lib/dns/tests/nsec3hash_unittest.cc')
-rw-r--r-- | src/lib/dns/tests/nsec3hash_unittest.cc | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc new file mode 100644 index 0000000..b47bb49 --- /dev/null +++ b/src/lib/dns/tests/nsec3hash_unittest.cc @@ -0,0 +1,269 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <string> + +#include <gtest/gtest.h> + +#include <boost/scoped_ptr.hpp> + +#include <dns/nsec3hash.h> +#include <dns/labelsequence.h> +#include <dns/rdataclass.h> +#include <util/encode/hex.h> + +using boost::scoped_ptr; +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using namespace isc::util::encode; + +namespace { +typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr; + +// Commonly used NSEC3 suffix, defined to reduce the amount of typing +const char* const nsec3_common = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"; + +class NSEC3HashTest : public ::testing::Test { +protected: + NSEC3HashTest() : + test_hash(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))), + test_hash_nsec3(NSEC3Hash::create(generic::NSEC3 + ("1 0 12 aabbccdd " + + string(nsec3_common)))) + { + const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd}; + test_hash_args.reset(NSEC3Hash::create(1, 12, salt, sizeof(salt))); + } + + ~NSEC3HashTest() { + // Make sure we reset the hash creator to the default + setNSEC3HashCreator(NULL); + } + + // 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 creation. + NSEC3HashPtr test_hash; + + // Similar to test_hash, but created from NSEC3 RR. + NSEC3HashPtr test_hash_nsec3; + + // Similar to test_hash, but created from passed args. + NSEC3HashPtr test_hash_args; +}; + +TEST_F(NSEC3HashTest, unknownAlgorithm) { + EXPECT_THROW(NSEC3HashPtr( + NSEC3Hash::create( + generic::NSEC3PARAM("2 0 12 aabbccdd"))), + UnknownNSEC3HashAlgorithm); + EXPECT_THROW(NSEC3HashPtr( + NSEC3Hash::create( + generic::NSEC3("2 0 12 aabbccdd " + + string(nsec3_common)))), + UnknownNSEC3HashAlgorithm); + + const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd}; + EXPECT_THROW(NSEC3HashPtr(NSEC3Hash::create(2, 12, salt, sizeof(salt))), + UnknownNSEC3HashAlgorithm); +} + +// Common checks for NSEC3 hash calculation +void +calculateCheck(NSEC3Hash& hash) { + // A couple of normal cases from the RFC5155 example. + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(Name("example"))); + EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL", + hash.calculate(Name("a.example"))); + + // Check case-insensitiveness + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(Name("EXAMPLE"))); + + // Repeat for the LabelSequence variant. + + // A couple of normal cases from the RFC5155 example. + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(LabelSequence(Name("example")))); + EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL", + hash.calculate(LabelSequence(Name("a.example")))); + + // Check case-insensitiveness + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(LabelSequence(Name("EXAMPLE")))); +} + +TEST_F(NSEC3HashTest, calculate) { + { + SCOPED_TRACE("calculate check with NSEC3PARAM based hash"); + calculateCheck(*test_hash); + } + { + SCOPED_TRACE("calculate check with NSEC3 based hash"); + calculateCheck(*test_hash_nsec3); + } + { + SCOPED_TRACE("calculate check with args based hash"); + calculateCheck(*test_hash_args); + } + + // Some boundary cases: 0-iteration and empty salt. Borrowed from the + // .com zone data. + EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM", + NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -"))) + ->calculate(Name("com"))); + EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM", + NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -"))) + ->calculate(LabelSequence(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", + NSEC3HashPtr(NSEC3Hash::create( + generic::NSEC3PARAM("1 0 256 AABBCCDD"))) + ->calculate(LabelSequence(Name("example.org")))); +} + +// Common checks for match cases +template <typename RDATAType> +void +matchCheck(NSEC3Hash& hash, const string& postfix) { + // If all parameters match, it's considered to be matched. + EXPECT_TRUE(hash.match(RDATAType("1 0 12 aabbccdd" + postfix))); + + // Algorithm doesn't match + EXPECT_FALSE(hash.match(RDATAType("2 0 12 aabbccdd" + postfix))); + // Iterations doesn't match + EXPECT_FALSE(hash.match(RDATAType("1 0 1 aabbccdd" + postfix))); + // Salt doesn't match + EXPECT_FALSE(hash.match(RDATAType("1 0 12 aabbccde" + postfix))); + // Salt doesn't match: the other has an empty salt + EXPECT_FALSE(hash.match(RDATAType("1 0 12 -" + postfix))); + // Flags don't matter + EXPECT_TRUE(hash.match(RDATAType("1 1 12 aabbccdd" + postfix))); +} + +TEST_F(NSEC3HashTest, matchWithNSEC3) { + { + SCOPED_TRACE("match NSEC3PARAM based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3>(*test_hash, " " + string(nsec3_common)); + } + { + SCOPED_TRACE("match NSEC3 based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3>(*test_hash_nsec3, + " " + string(nsec3_common)); + } +} + +TEST_F(NSEC3HashTest, matchWithNSEC3PARAM) { + { + SCOPED_TRACE("match NSEC3PARAM based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3PARAM>(*test_hash, ""); + } + { + SCOPED_TRACE("match NSEC3 based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3PARAM>(*test_hash_nsec3, ""); + } +} + +// A simple faked hash calculator and a dedicated creator for it. +class TestNSEC3Hash : public NSEC3Hash { + virtual string calculate(const Name&) const { + return ("00000000000000000000000000000000"); + } + virtual string calculate(const LabelSequence&) const { + return ("00000000000000000000000000000000"); + } + virtual bool match(const generic::NSEC3PARAM&) const { + return (true); + } + virtual bool match(const generic::NSEC3&) const { + return (true); + } +}; + +// This faked creator basically creates the faked calculator regardless of +// the passed NSEC3PARAM or NSEC3. But if the most significant bit of flags +// is set, it will behave like the default creator. +class TestNSEC3HashCreator : public NSEC3HashCreator { +public: + virtual NSEC3Hash* create(const generic::NSEC3PARAM& param) const { + if ((param.getFlags() & 0x80) != 0) { + return (default_creator_.create(param)); + } + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(const generic::NSEC3& nsec3) const { + if ((nsec3.getFlags() & 0x80) != 0) { + return (default_creator_.create(nsec3)); + } + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(uint8_t, uint16_t, + const uint8_t*, size_t) const { + isc_throw(isc::Unexpected, + "This method is not implemented here."); + } +private: + DefaultNSEC3HashCreator default_creator_; +}; + +TEST_F(NSEC3HashTest, setCreator) { + // Re-check an existing case using the default creator/hash implementation + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + + // Replace the creator, and confirm the hash values are faked + TestNSEC3HashCreator test_creator; + setNSEC3HashCreator(&test_creator); + // Re-create the hash object with the new creator + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(Name("example"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(LabelSequence(Name("example")))); + // Same for hash from NSEC3 RDATA + test_hash.reset(NSEC3Hash::create(generic::NSEC3 + ("1 0 12 aabbccdd " + + string(nsec3_common)))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(Name("example"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(LabelSequence(Name("example")))); + + // If we set a special flag big (0x80) on creation, it will act like the + // default creator. + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM( + "1 128 12 aabbccdd"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + test_hash.reset(NSEC3Hash::create(generic::NSEC3 + ("1 128 12 aabbccdd " + + string(nsec3_common)))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + + // Reset the creator to default, and confirm that + setNSEC3HashCreator(NULL); + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); +} + +} // end namespace |