]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[sedhcpv6] began parser (checkpoint)
authorFrancis Dupont <fdupont@isc.org>
Thu, 28 May 2015 18:14:16 +0000 (20:14 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 28 May 2015 18:14:16 +0000 (20:14 +0200)
doc/guide/dhcp6-srv.xml
src/bin/dhcp6/dhcp6.spec
src/lib/cryptolink/crypto_asym.h
src/lib/cryptolink/cryptolink.h
src/lib/dhcpsrv/Makefile.am
src/lib/dhcpsrv/parsers/dbaccess_parser.h
src/lib/dhcpsrv/parsers/sedhcp6_parser.cc [new file with mode: 0644]
src/lib/dhcpsrv/parsers/sedhcp6_parser.h [new file with mode: 0644]
src/lib/dhcpsrv/tests/Makefile.am
src/lib/dhcpsrv/tests/d2_client_unittest.cc
src/lib/dhcpsrv/tests/sedhcp6_parser_unittest.cc [new file with mode: 0644]

index d10e99c39db230b3839f4d51389235597c338b55..80bb50985bcda5c4c9a703bba95c7567d1800c0c 100644 (file)
@@ -2221,6 +2221,34 @@ should include options from the isc option space:
       </para>
    </section>
 
+   <section id="reservation6-secure">
+      <title>Secure DHCPv6 Authorization in Host Reservation</title>
+
+      <note>Secure DHCPv6 is experimental</note>
+
+      <para>Host reservations are used too in secure DHCPv6 to provide
+      the authorization, i.e., a way the check the association between
+      the public key or the certificate presented in a packet protected
+      by secure DHCPv6 and a particular client.
+      </para>
+
+      <para>A client is configured in a host reservation with the
+      filename of its public key or certificate (the two keywords can
+      be used indifferently: the key type always follows the secure DHCPv6
+      option type). The two 
+      <itemizedlist>
+      <listitem><simpara> <command>public-key</command> specifies
+      the name of a file containing the public key.
+      </simpara></listitem>
+      <listitem><simpara> <command>certificate</command> specifies
+      the name of a file containing the certificate.
+      </simpara></listitem>
+      </itemizedlist>
+      The format of the file content must follow the checking operation,
+      e.g., when it is a bit-to-bit compare it must be encoded in DER.
+      </para>
+   </section>
+
     <!-- @todo: add support for per IA reservation (that specifies IAID in
                 the ip-addresses and prefixes) -->
   </section>
index 609f368240882c276c48e861d78eaec281268f4e..76974ed2127ec29cbb02ea9646c885cf87c8ef53 100644 (file)
         ]
       },
 
+      { "item_name": "secure-Dhcp6",
+        "item_type": "map",
+        "item_optional": true,
+        "item_default": {},
+        "map_item_spec": [
+        {
+            "item_name": "sign-answers",
+            "item_type": "boolean",
+            "item_optional": true;
+            "item_default": false
+        },
+        {
+            "item_name": "signature-algorithm"
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": "RSA"
+        },
+        {
+            "item_name": "hash-algorithm"
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": "SHA-256"
+        },
+        {
+            "item_name": "public-key"
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": ""
+        },
+        {
+            "item_name": "certificate"
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": ""
+        },
+        {
+            "item_name": "timestamp-answers",
+            "item_type": "boolean",
+            "item_optional": true,
+            "item_default": false
+        },
+        {
+            "item_name": "check-signatures",
+            "item_type": "boolean",
+            "item_optional": true,
+            "item_default": false
+        },
+        {
+            "item_name": "check-timestamps",
+            "item_type": "boolean",
+            "item_optional": true,
+            "item_default": false
+        },
+        {
+            "item_name": "check-authorizations",
+            "item_type": "boolean",
+            "item_optional": true,
+            "item_default": false
+        },
+        {
+            "item_name": "validation-policy"
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": "offline"
+        }
+      },
       { "item_name": "renew-timer",
         "item_type": "integer",
         "item_optional": false,
                             "item_optional": false,
                             "item_default": ""
                         }
+                      },
+                      {
+                        "item_name": "public-key",
+                        "item_type": "string",
+                        "item_optional": true,
+                        "item_default": ""
+                      },
+                      {
+                        "item_name": "certificate",
+                        "item_type": "string",
+                        "item_optional": true,
+                        "item_default": ""
                       } ]
                   }
                 },
index d1411ba08d8afc3a31da00a0a7e599e6cf5d9804..98fd6ad1e5926ba2f718d183de55e361e85e0b15 100644 (file)
@@ -40,7 +40,6 @@ private:
     /// \exception UnsupportedAlgorithm if the given algorithm
     ///            is unknown or not supported by the underlying library
     /// \exception Badkey if the given key length is bad
-    /// \exception InvalidCert if the certification fails to validate
     /// \exception LibraryError if there was any unexpected exception
     ///            in the underlying library
     ///
index ce60ba159f39de364c9905c982f559998e8b4d70..029616bf5a9823c9707132fbbf1db41d0d145fd4 100644 (file)
@@ -106,13 +106,6 @@ public:
         CryptoLinkError(file, line, what) {}
 };
 
-/// This exception is thrown when a certification is invalid
-class InvalidCert : public CryptoLinkError {
-public:
-   InvalidCert(const char* file, size_t line, const char* what) :
-       CryptoLinkError(file, line, what) {}
-};
-
 /// This exception is raised when a general error that was not
 /// specifically caught is thrown by the underlying library. It
 /// is replaced by this one so as not have 'external' exceptions
@@ -265,7 +258,6 @@ public:
     /// \exception UnsupportedAlgorithm if the given algorithm
     ///            is unknown or not supported by the underlying library
     /// \exception BadKey if the given key length is bad
-    /// \exception InvalidCert if the certification fails to validate
     /// \exception LibraryError if there was any unexpected exception
     ///            in the underlying library
     /// \param key            The key to sign or verify with
index 2f8f6097e0f5739deb1be7403ce4aec63f1fb317..a823f4c81fed25604233e4dab63d69d8e5082f17 100644 (file)
@@ -33,6 +33,8 @@ EXTRA_DIST += parsers/dhcp_parsers.h
 EXTRA_DIST += parsers/host_reservation_parser.cc
 EXTRA_DIST += parsers/host_reservation_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
+EXTRA_DIST += parsers/sedhcp6_parser.cc
+EXTRA_DIST += parsers/sedhcp6_parser.h
 
 # Define rule to build logging source files from message file
 dhcpsrv_messages.h dhcpsrv_messages.cc hosts_messages.h hosts_messages.cc: s-messages
@@ -127,7 +129,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
-
+libkea_dhcpsrv_la_SOURCES += parsers/sedhcp6_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/sedhcp6_parser.h
 
 nodist_libkea_dhcpsrv_la_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
 nodist_libkea_dhcpsrv_la_SOURCES += hosts_messages.h hosts_messages.cc
index 2d81b9dde986bf4da8bb5f31925db474b2c21415..797dd5d8884a432bebfc5c4855b36eb49fca7c21 100644 (file)
@@ -96,7 +96,7 @@ public:
     /// Creates an instance of this parser.
     ///
     /// @param param_name Name of the parameter used to access the
-    ///        configuration.
+    ///         configuration.
     /// @param ctx Parser context.
     ///
     /// @return Pointer to a DbAccessParser.  The caller is responsible for
diff --git a/src/lib/dhcpsrv/parsers/sedhcp6_parser.cc b/src/lib/dhcpsrv/parsers/sedhcp6_parser.cc
new file mode 100644 (file)
index 0000000..e97613e
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright (C) 2015 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 <config.h>
+
+#include <dhcp/option.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/parsers/sedhcp6_parser.h>
+
+#include <boost/foreach.hpp>
+
+#include <map>
+#include <string>
+#include <utility>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+// Factory function to build the parser
+SeDhcp6Parser::SeDhcp6Parser(const std::string&, const ParserContext& ctx)
+    : values_(), ctx_(ctx)
+{
+}
+
+// Parse the configuration and check that the various keywords are consistent.
+void
+SeDhcp6Parser::build(isc::data::ConstElementPtr config_value) {
+
+    // DHCPv6 only
+    if (ctx_.universe_ != Option::V6) {
+        isc_throw(DhcpConfigError,
+                  "secure-Dhcp6 makes sense only for DHCPv6");
+    }
+
+    // To cope with incremental updates, the strategy is:
+    // 1. Take a copy of the stored keyword/value pairs.
+    // 2. Update the copy with the passed keywords.
+    // 3. Perform validation checks on the updated keyword/value pairs.
+    // 4. If all is OK, update the stored keyword/value pairs.
+
+    // 1. Take a copy of the stored keyword/value pairs.
+    std::map<string, string> values_copy = values_;
+
+    // 2. Update the copy with the passed keywords.
+    BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
+        bool found = false;
+        try {
+            // Booleans
+            if ((param.first == "sign-answers") ||
+                (param.first == "timestamp-answers") ||
+                (param.first == "check-authorizations") ||
+                (param.first == "check-signatures") ||
+                (param.first == "check-timestamps")) {
+                found = true;
+                values_copy[param.first] = (param.second->boolValue() ?
+                                            "true" : "false");
+            } else if ((param.first == "signature-algorithm") ||
+                       (param.first == "hash-algorithm") ||
+                       (param.first == "public-key") ||
+                       (param.first == "certificate") ||
+                       (param.first == "validation-policy")) {
+                found = true;
+                values_copy[param.first] = param.second->stringValue();
+            }
+        } catch (const isc::data::TypeError& ex) {
+            // Append position of the element.
+            isc_throw(isc::data::TypeError, ex.what() << " ("
+                      << param.second->getPosition() << ")");
+        }
+        if (!found) {
+            isc_throw(DhcpConfigError,
+                      "secure-Dhcp6 parameter not supported: "
+                      << param.first << " ("
+                      << param.second->getPosition() << ")");
+        }
+    }
+
+    // 3. Perform validation checks on the updated set of keyword/values.
+    //
+    // a. Check if we sign
+    bool sign_answers = false;
+    StringPairMap::const_iterator sign_answers_ptr =
+        values_copy.find("sign-answers");
+    if ((sign_answers_ptr != values_copy.end()) &&
+        (sign_answers_ptr->second == "true")) {
+        sign_answers = true;
+    }
+
+    // b. Check if we can sign
+    string public_key = "";
+    StringPairMap::const_iterator public_key_ptr =
+        values_copy.find("public-key");
+    if (public_key_ptr != values_copy.end()) {
+        public_key = public_key_ptr->second;
+    }
+    string certificate = "";
+    StringPairMap::const_iterator certificate_ptr =
+        values_copy.find("certificate");
+    if (certificate_ptr != values_copy.end()) {
+        certificate = certificate_ptr->second;
+    }
+    if (sign_answers && public_key.empty() && certificate.empty()) {
+        isc_throw(DhcpConfigError,
+                  "secure-Dhcp6 requires a public-key or a certificate "
+                  "when sign-answers is enabled");
+    }
+    if (!public_key.empty() && !certificate.empty()) {
+        isc_throw(DhcpConfigError,
+                  "public-key and certificate cannot be both defined");
+    }
+
+    // c. Check if the signature algorithm is known
+    StringPairMap::const_iterator signature_algorithm_ptr =
+        values_copy.find("signature-algorithm");
+    if ((signature_algorithm_ptr != values_copy.end()) &&
+        (signature_algorithm_ptr->second != "RSA")) {
+        isc_throw(DhcpConfigError, "unknown signature-algorithm: "
+                  << signature_algorithm_ptr->second);
+    }
+
+    // d. Check if the hash algorithm is known
+    StringPairMap::const_iterator hash_algorithm_ptr =
+        values_copy.find("hash-algorithm");
+    if ((hash_algorithm_ptr != values_copy.end()) &&
+        (hash_algorithm_ptr->second != "SHA-256") &&
+        (hash_algorithm_ptr->second != "SHA-512")) {
+        isc_throw(DhcpConfigError, "unknown hash-algorithm: "
+                  << hash_algorithm_ptr->second);
+    }
+
+    // e. Check if the validation policy is known
+    StringPairMap::const_iterator validation_policy_ptr =
+        values_copy.find("validation-policy");
+    if ((validation_policy_ptr != values_copy.end()) &&
+        (validation_policy_ptr->second != "offline") &&
+        (validation_policy_ptr->second != "online")) {
+        isc_throw(DhcpConfigError, "unknown validation-policy: "
+                  << validation_policy_ptr->second);
+    }
+
+    // 4. If all is OK, update the stored keyword/value pairs.  We do this by
+    // swapping contents - values_copy is destroyed immediately after the
+    // operation (when the method exits), so we are not interested in its new
+    // value.
+    values_.swap(values_copy);
+}
+
+// Create TODO
+
+// Commit the changes - create a new secure DHCPv6 state with new parameters
+void
+SeDhcp6Parser::commit() {
+    // TODO
+}
+
+};  // namespace dhcp
+};  // namespace isc
diff --git a/src/lib/dhcpsrv/parsers/sedhcp6_parser.h b/src/lib/dhcpsrv/parsers/sedhcp6_parser.h
new file mode 100644 (file)
index 0000000..de5ff53
--- /dev/null
@@ -0,0 +1,116 @@
+// Copyright (C) 2015 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 SEDHCP6_PARSER_H
+#define SEDHCP6_PARSER_H
+
+#include <cc/data.h>
+#include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Secure DHCPv6 Parameters
+///
+/// This class is the parser for the secure DHCPv6 configuration.  This is a
+/// map under the top-level "secure" element, and comprises a map of
+/// strings.
+class SeDhcp6Parser: public DhcpConfigParser {
+public:
+    /// @brief Keyword and associated value
+    typedef std::pair<std::string, std::string> StringPair;
+
+    /// @brief Keyword/value collection of secure DHCPv6 parameters
+    typedef std::map<std::string, std::string> StringPairMap;
+
+    /// @brief Constructor
+    ///
+    /// @param param_name Name of the parameter under which the secure
+    ///        DHCPv6 details are held.
+    /// @param ctx Parser context.
+    SeDhcp6Parser(const std::string& param_name, const ParserContext& ctx);
+
+    /// The destructor.
+    virtual ~SeDhcp6Parser()
+    {}
+
+    /// @brief Prepare configuration value.
+    ///
+    /// Parses the set of strings forming the secure DHCPv6 specification and
+    /// checks that all are OK.  In particular it checks:
+    ///
+    /// TODO
+    ///
+    /// Once all has been validated, TODO
+    ///
+    /// @param config_value The configuration value for the "secure""
+    ///        identifier.
+    ///
+    /// @throw TODO
+    virtual void build(isc::data::ConstElementPtr config_value);
+
+    /// @brief Apply the prepared configuration value to the server.
+    ///
+    /// With the string validated, TODO
+    ///
+    /// This method is expected to be called after \c build(), and only once.
+    /// The result is undefined otherwise.
+    virtual void commit();
+
+    /// @brief Factory method to create parser
+    ///
+    /// Creates an instance of this parser.
+    ///
+    /// @param param_name Name of the parameter used to access the
+    ///         configuration.
+    /// @param ctx Parser context.
+    ///
+    /// @return Pointer to a SeDhcp6Parser.  The caller is responsible for
+    ///         destroying the parser after use.
+    static DhcpConfigParser* factory(const std::string& param_name,
+                                     const ParserContext& ctx) {
+        return (new SeDhcp6Parser(param_name, ctx));
+    }
+
+protected:
+    /// @brief Get secure DHCPv6 parameters
+    ///
+    /// Used in testing to check that the configuration information has been
+    /// parsed correctly.
+    ///
+    /// @return Reference to the internal map of keyword/value pairs
+    ///         representing secure DHCPv6 information.  This is valid only
+    ///         for so long as the the parser remains in existence.
+    const StringPairMap& getSeDhcp6Parameters() const {
+        return (values_);
+    }
+
+    /// TODO
+
+private:
+
+    std::map<std::string, std::string> values_; ///< Stored parameter values
+
+    ParserContext ctx_; ///< Parser context
+};
+
+};  // namespace dhcp
+};  // namespace isc
+
+
+#endif // SEDHCP6_PARSER_H
index 36334dd1bdd24880379342fdc4a7760ac36e8726..a0e70c06d803e9b298163a911bd33445654a0cfa 100644 (file)
@@ -98,6 +98,7 @@ endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
 libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
+libdhcpsrv_unittests_SOURCES += sedhcp6_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
index 2390027b7c64d70e1b02863c41ac624efbbaa2bf..1fa015b4936a45bfa8d462f27437b9c31069c63e 100644 (file)
@@ -618,7 +618,7 @@ TEST(D2ClientMgr, qualifyName) {
     EXPECT_EQ("somehost.suffix.com.", qualified_name);
 
 
-       //append suffix but dot
+        //append suffix but dot
     ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
                                   isc::asiolink::IOAddress("127.0.0.1"), 477,
                                   isc::asiolink::IOAddress("127.0.0.1"), 478,
@@ -632,7 +632,7 @@ TEST(D2ClientMgr, qualifyName) {
     EXPECT_EQ("somehost.suffix.com", qualified_name);
 
 
-       //append no suffix and not dot
+        //append no suffix and not dot
     ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
                                   isc::asiolink::IOAddress("127.0.0.1"), 477,
                                   isc::asiolink::IOAddress("127.0.0.1"), 478,
diff --git a/src/lib/dhcpsrv/tests/sedhcp6_parser_unittest.cc b/src/lib/dhcpsrv/tests/sedhcp6_parser_unittest.cc
new file mode 100644 (file)
index 0000000..01ee425
--- /dev/null
@@ -0,0 +1,256 @@
+// Copyright (C) 2015 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 <config.h>
+
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/sedhcp6_parser.h>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture class for @c SeDhcp6Parser
+class SeDhcp6ParserTest : public ::testing::Test {
+protected:
+
+    /// @brief Setup for each test.
+    ///
+    /// Clears the configuration in the @c CfgMgr.
+    virtual void SetUp();
+
+    /// @brief Cleans up after each test.
+    ///
+    /// Clears the configuration in the @c CfgMgr.
+    virtual void TearDown();
+
+};
+
+void
+SeDhcp6ParserTest::SetUp() {
+    CfgMgr::instance().clear();
+}
+
+void
+SeDhcp6ParserTest::TearDown() {
+    CfgMgr::instance().clear();
+}
+
+// This test checks the parser on a basic input
+TEST_F(SeDhcp6ParserTest, dhcpv6Universe) {
+    std::string config = "{ \"sign-answers\": false }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+}
+
+// This test checks the parser requires DHCPv6 context
+TEST_F(SeDhcp6ParserTest, dhcpv4Universe) {
+    std::string config = "{ \"sign-answers\": false }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V4));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test checks unknown parameters
+TEST_F(SeDhcp6ParserTest, unknownParameters) {
+    std::string config = "{ \"unknown-boolean\": true }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+
+    config = "{ \"unknown-string\": \"foobar\" }";
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+
+    config = "{ \"unknown-integer\": 1234 }";
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test checks the boolean parameters
+TEST_F(SeDhcp6ParserTest, booleanParameters) {
+    std::string config = "{ \"sign-answers\": false }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"sign-answers\": \"foo\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+
+    config = "{ \"timestamp-answers\": true }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"timestamp-answers\": \"foobar\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+
+    config = "{ \"check-authorizations\": true }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"check-authorizations\": \"foobar\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+
+    config = "{ \"check-signatures\": true }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"check-signatures\": \"foobar\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+
+    config = "{ \"check-timestamps\": true }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"check-timestamps\": \"foobar\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+}
+
+// This test checks string parameters
+TEST_F(SeDhcp6ParserTest, stringParameters) {
+    std::string config = "{ \"public-key\": \"foobar\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"public-key\": true }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), TypeError);
+
+    config = "{ \"certificate\": \"foobar\" }";
+    config_element = Element::fromJSON(config);
+    SeDhcp6Parser parser2("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser2.build(config_element));
+
+    config = "{ \"certificate\": false }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser2.build(config_element), TypeError);
+
+    // other string parameters have constrainted values
+}
+
+// This test checks algorithm values
+TEST_F(SeDhcp6ParserTest, algorithms) {
+    std::string config = "{ \"signature-algorithm\": \"RSA\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"signature-algorithm\": \"DSA\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+
+    config = "{ \"hash-algorithm\": \"SHA-256\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"hash-algorithm\": \"SHA-512\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"hash-algorithm\": \"MD5\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+
+    // Verify the default
+}
+
+// This test checks enabled signing of answers
+TEST_F(SeDhcp6ParserTest, signing) {
+    std::string config = "{ \"sign-answers\": false }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    // Not public key and certificate at the same time
+    // Need real files!
+    config = "{ \"public-key\": \"my-pub-key\","
+        " \"certificate\": \"my-certificate\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+
+    // Either public key or certificate when signing is enabled
+    config = "{  \"sign-answers\": true }";
+    config_element = Element::fromJSON(config);
+    SeDhcp6Parser parser2("secure-Dhcp6", *parser_context);
+    ASSERT_THROW(parser2.build(config_element), DhcpConfigError);
+
+    config = "{  \"sign-answers\": true,"
+        " \"public-key\": \"my-pub-key\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser2.build(config_element));
+
+    config = "{  \"sign-answers\": true,"
+        " \"certificate\": \"my-certificate\" }";
+    config_element = Element::fromJSON(config);
+    SeDhcp6Parser parser3("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser3.build(config_element));
+}
+
+// This test checks the parsing of validation policy syntax
+TEST_F(SeDhcp6ParserTest, validationPolicy) {
+    std::string config = "{ \"validation-policy\": \"offline\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    ParserContextPtr parser_context(new ParserContext(Option::V6));
+
+    SeDhcp6Parser parser("secure-Dhcp6", *parser_context);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"validation-policy\": \"online\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    config = "{ \"validation-policy\": \"forever\" }";
+    config_element = Element::fromJSON(config);
+    ASSERT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+} // end of anonymous namespace