Browse Source

[3036] Added a function which creates DHCID from DUID and FQDN.

Marcin Siodelski 11 years ago
parent
commit
3e90051aa1
4 changed files with 113 additions and 1 deletions
  1. 1 1
      src/bin/d2/Makefile.am
  2. 57 0
      src/bin/d2/ncr_msg.cc
  3. 16 0
      src/bin/d2/ncr_msg.h
  4. 39 0
      src/bin/d2/tests/ncr_unittests.cc

+ 1 - 1
src/bin/d2/Makefile.am

@@ -2,7 +2,7 @@ SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
-AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 if USE_CLANGPP

+ 57 - 0
src/bin/d2/ncr_msg.cc

@@ -16,6 +16,8 @@
 #include <asiolink/io_address.h>
 #include <asiolink/io_error.h>
 
+#include <botan/sha2_32.h>
+
 #include <sstream>
 #include <limits>
 
@@ -31,6 +33,12 @@ D2Dhcid::D2Dhcid(const std::string& data) {
     fromStr(data);
 }
 
+D2Dhcid::D2Dhcid(const isc::dhcp::DUID& duid,
+                 const std::vector<uint8_t>& wire_fqdn) {
+    fromDUID(duid, wire_fqdn);
+}
+
+
 void
 D2Dhcid::fromStr(const std::string& data) {
     bytes_.clear();
@@ -44,8 +52,57 @@ D2Dhcid::fromStr(const std::string& data) {
 std::string
 D2Dhcid::toStr() const {
     return (isc::util::encode::encodeHex(bytes_));
+}
 
+void
+D2Dhcid::fromDUID(const isc::dhcp::DUID& duid,
+                  const std::vector<uint8_t>& wire_fqdn) {
+    // DHCID created from DUID starts with two bytes representing
+    // a type of the identifier. The value of 0x0002 indicates that
+    // DHCID has been created from DUID. The 3rd byte is equal to 1
+    // which indicates that the SHA-256 algorithm is used to create
+    // a DHCID digest. This value is called digest-type.
+    static uint8_t dhcid_header[] = { 0x00, 0x02, 0x01 };
+
+    // We get FQDN in the wire format, so we don't know if it is
+    // valid. It is caller's responsibility to make sure it is in
+    // the valid format. Here we just make sure it is not empty.
+    if (wire_fqdn.empty()) {
+        isc_throw(isc::d2::NcrMessageError,
+                  "empty FQDN used to create DHCID");
+    }
+
+    // Get the wire representation of the DUID.
+    std::vector<uint8_t> data = duid.getDuid();
+    // It should be DUID class responsibility to validate the DUID
+    // but let's be on the safe side here and make sure that empty
+    // DUID is not returned.
+    if (data.empty()) {
+        isc_throw(isc::d2::NcrMessageError,
+                  "empty DUID used to create DHCID");
+    }
+
+    // Append FQDN in the wire format.
+    data.insert(data.end(), wire_fqdn.begin(), wire_fqdn.end());
+
+    // Use the DUID and FQDN to compute the digest (see RFC4701, section 3).
+    Botan::SecureVector<Botan::byte> secure;
+    try {
+        Botan::SHA_256 sha;
+        // We have checked already that the DUID and FQDN aren't empty
+        // so it is safe to assume that the data buffer is not empty.
+        secure = sha.process(static_cast<const Botan::byte*>(&data[0]),
+                             data.size());
+    } catch (const std::exception& ex) {
+        isc_throw(isc::d2::NcrMessageError,
+                  "error while generating DHCID from DUID: "
+                  << ex.what());
+    }
 
+    // The exception unsafe part is finished, so we can finally replace
+    // the contents of bytes_.
+    bytes_.assign(dhcid_header, dhcid_header + sizeof(dhcid_header));
+    bytes_.insert(bytes_.end(), secure.begin(), secure.end());
 }
 
 

+ 16 - 0
src/bin/d2/ncr_msg.h

@@ -21,6 +21,7 @@
 /// DHCP-DDNS. These requests are referred to as NameChangeRequests.
 
 #include <cc/data.h>
+#include <dhcp/duid.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <util/encode/hex.h>
@@ -77,6 +78,14 @@ public:
     /// or there is an odd number of digits.
     D2Dhcid(const std::string& data);
 
+    /// @brief Constructor, creates an instance of the @c D2Dhcid from the
+    /// @c isc::dhcp::DUID.
+    ///
+    /// @param duid An object representing DUID.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    D2Dhcid(const isc::dhcp::DUID& duid,
+            const std::vector<uint8_t>& wire_fqdn);
+
     /// @brief Returns the DHCID value as a string of hexadecimal digits.
     ///
     /// @return returns a string containing a contiguous stream of digits.
@@ -92,6 +101,13 @@ public:
     /// or there is an odd number of digits.
     void fromStr(const std::string& data);
 
+    /// @brief Sets the DHCID value based on the DUID.
+    ///
+    /// @param duid A @c isc::dhcp::DUID object encapsulating DUID.
+    /// @param wire_fqdn A on-wire canonical representation of the FQDN.
+    void fromDUID(const isc::dhcp::DUID& duid,
+                  const std::vector<uint8_t>& wire_fqdn);
+
     /// @brief Returns a reference to the DHCID byte vector.
     ///
     /// @return returns a reference to the vector.

+ 39 - 0
src/bin/d2/tests/ncr_unittests.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <d2/ncr_msg.h>
+#include <dhcp/duid.h>
 #include <util/time_utilities.h>
 
 #include <gtest/gtest.h>
@@ -21,6 +22,7 @@
 using namespace std;
 using namespace isc;
 using namespace isc::d2;
+using namespace isc::dhcp;
 
 namespace {
 
@@ -273,6 +275,43 @@ TEST(NameChangeRequestTest, dhcidTest) {
     EXPECT_EQ(test_str, next_str);
 }
 
+/// Tests that DHCID is correctly created from a DUID and FQDN. The final format
+/// of the DHCID is as follows:
+/// <identifier-type> <digest-type-code> <digest>
+/// where:
+/// - identifier-type (2 octets) is 0x0002.
+/// - digest-type-code (1 octet) indicates SHA-256 hashing and is equal 0x1.
+/// - digest = SHA-256(<DUID> <FQDN>)
+/// Note: FQDN is given in the on-wire canonical format.
+TEST(NameChangeRequestTest, dhcidFromDUID) {
+    D2Dhcid dhcid;
+
+    // Create DUID.
+    uint8_t duid_data[] = { 0, 1, 2, 3, 4, 5, 6 };
+    DUID duid(duid_data, sizeof(duid_data));
+
+    // Create FQDN in on-wire format: myhost.example.com. It is encoded
+    // as a set of labels, each preceded by its length. The whole FQDN
+    // is zero-terminated.
+    const uint8_t fqdn_data[] = {
+        6, 109, 121, 104, 111, 115, 116,     // myhost.
+        7, 101, 120, 97, 109, 112, 108, 101, // example.
+        3, 99, 111, 109, 0                   // com.
+    };
+    std::vector<uint8_t> wire_fqdn(fqdn_data, fqdn_data + sizeof(fqdn_data));
+
+    // Create DHCID.
+    ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn));
+
+    // The reference DHCID (represented as string of hexadecimal digits)
+    // has been calculated using one of the online calculators.
+    std::string dhcid_ref = "0002012191B7B21AF97E0E656DF887C5E2D"
+        "EF30E7758A207EDF4CCB2DE8CA37066021C";
+
+    // Make sure that the DHCID is valid.
+    EXPECT_EQ(dhcid_ref, dhcid.toStr());
+}
+
 /// @brief Verifies the fundamentals of converting from and to JSON.
 /// It verifies that:
 /// 1. A NameChangeRequest can be created from a valid JSON string.