<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
+ option type). The two parameters are:
<itemizedlist>
<listitem><simpara> <command>public-key</command> specifies
- the name of a file containing the public key.
+ the name of a file containing the public key in PEM format.
</simpara></listitem>
<listitem><simpara> <command>certificate</command> specifies
- the name of a file containing the certificate.
+ the name of a file containing the certificate in PEM format.
</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>
try {
key->update(tbs->rawBegin(), tbs->rawEnd() - tbs->rawBegin());
key->sign(start + sig_off, sig_len, BASIC);
+ key->clear();
} catch (const Exception& ex) {
ostringstream sigmsg("signature sign failed: ");
sigmsg << ex.what();
LOG_ERROR(dhcp6_logger, SEDHCP6_SIGNATURE_FINALIZE_FAIL)
.arg("signature sign failed?!");
}
+ vector<uint8_t> dump(sig_len);
+ memcpy(&dump[0], start + sig_off, sig_len);
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, SEDHCP6_SIGNATURE_DUMP)
+ .arg(encode::encodeHex(dump));
}
};
This is a "should not happen" condition which reflects an internal
error.
+% SEDHCP6_SIGNATURE_DUMP final signature: %1
+This debug message dumps the final content of the signature.
+
% SEDHCP6_SIGNATURE_FINALIZE_FAIL signature finalize error: %1
This error message indicates that the signature finalize has failed.
This is a "should not happen" condition which reflects an internal
}
}
+ /// \brief Clear the crypto state and go back to the initial state
+ /// (must be called before reusing an Asym object)
+ void clear() {
+ std::string hash = btn::getHashAlgorithmName(hash_);
+ if (hash.compare("Unknown") == 0) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ std::string emsa = "EMSA3(" + hash + ")";
+ if (kind_ == PRIVATE) {
+ try {
+ signer_.reset(new Botan::PK_Signer(*priv_, emsa));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "PK_Signer: " << exc.what());
+ }
+ } else {
+ try {
+ verifier_.reset(new Botan::PK_Verifier(*pub_, emsa));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "PK_Verifier: " << exc.what());
+ }
+ }
+ }
+
/// @brief Export the key value (binary)
///
/// See @ref isc::cryptolink::Asym::exportkey() for details
return (impl_->verify(sig, len, sig_format));
}
+void
+Asym::clear() {
+ impl_->clear();
+}
+
std::vector<uint8_t>
Asym::exportkey(const AsymKeyKind key_kind,
const AsymFormat key_format) const {
}
HashAlgorithm
-Hash:getHashAlgorithm() const {
+Hash::getHashAlgorithm() const {
return (impl_->getHashAlgorithm());
}
}
HashAlgorithm
-HMAC:getHashAlgorithm() const {
+HMAC::getHashAlgorithm() const {
return (impl_->getHashAlgorithm());
}
/// called multiple times with different signatures.
bool verify(const void* sig, size_t len, const AsymFormat sig_format);
+ /// \brief Clear the crypto state and go back to the initial state
+ /// (must be called before reusing an Asym object)
+ void clear();
+
/// \brief Export the key value
///
/// The result will be returned as a std::vector<uint8_t>
}
}
+ /// @brief Clear the crypto state and go back to the initial state
+ /// (must be called before reusing an Asym object)
+ void clear() {
+ if (mdctx_) {
+ EVP_MD_CTX_cleanup(mdctx_.get());
+ } else {
+ mdctx_.reset(new EVP_MD_CTX);
+ }
+ EVP_MD_CTX_init(mdctx_.get());
+ const EVP_MD* md = ossl::getHashAlgorithm(hash_);
+ if (md == 0) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
+ EVP_MD_CTX_cleanup(mdctx_.get());
+ EVP_PKEY_free(pkey_);
+ pkey_ =NULL;
+ isc_throw(LibraryError, "EVP_DigestInit_ex");
+ }
+ }
+
/// @brief Export the key value (binary)
///
/// See @ref isc::cryptolink::Asym::exportkey() for details
return (impl_->verify(sig, len, sig_format));
}
+void
+Asym::clear() {
+ impl_->clear();
+}
+
std::vector<uint8_t>
Asym::exportkey(const AsymKeyKind key_kind,
const AsymFormat key_format) const {
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014, 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
deleteAsym);
EXPECT_FALSE(bad_bin->validate());
}
+
+//
+// Multiple signatures
+//
+TEST(AsymTest, doubleSign) {
+ std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+ // Sign it
+ boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfile, "1234",
+ RSA_, SHA1,
+ PRIVATE, ASN1),
+ deleteAsym);
+ ASSERT_TRUE(rsa_sign);
+
+ OutputBuffer sig1(1);
+ size_t sig1_len = rsa_sign->getSignatureLength(BASIC);
+ EXPECT_EQ(128, sig1_len);
+ rsa_sign->update(data_buf.getData(), data_buf.getLength());
+ rsa_sign->sign(sig1, sig1_len, BASIC);
+ ASSERT_EQ(sig1_len, sig1.getLength());
+
+ // Clear state
+ rsa_sign->clear();
+
+ // Sign it again
+ OutputBuffer sig2(1);
+ size_t sig2_len = rsa_sign->getSignatureLength(BASIC);
+ EXPECT_EQ(128, sig2_len);
+ rsa_sign->update(data_buf.getData(), data_buf.getLength());
+ rsa_sign->sign(sig2, sig2_len, BASIC);
+ EXPECT_EQ(sig2_len, sig2.getLength());
+
+ // Compare
+ ASSERT_EQ(sig1_len, sig2_len);
+ EXPECT_TRUE(std::memcmp(sig1.getData(), sig2.getData(), sig1_len) == 0);
+}
+
+//
+// Multiple verifies
+//
+TEST(AsymTest, doubleVerify) {
+ std::string data = "Kea provides DHCPv4 and DHCPv6 servers";
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+ // Sign it
+ boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfile, "1234",
+ RSA_, SHA1,
+ PRIVATE, ASN1),
+ deleteAsym);
+ ASSERT_TRUE(rsa_sign);
+
+ OutputBuffer sig(1);
+ size_t sig_len = rsa_sign->getSignatureLength(BASIC);
+ EXPECT_EQ(128, sig_len);
+ rsa_sign->update(data_buf.getData(), data_buf.getLength());
+ rsa_sign->sign(sig, sig_len, BASIC);
+ EXPECT_EQ(sig_len, sig.getLength());
+
+ // Verify
+ boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfile, "",
+ RSA_, SHA1,
+ PUBLIC, ASN1),
+ deleteAsym);
+ ASSERT_TRUE(rsa_verify);
+
+ rsa_verify->update(data_buf.getData(), data_buf.getLength());
+ EXPECT_TRUE(rsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+
+ // Clear state
+ rsa_verify->clear();
+
+ // Verify again
+ rsa_verify->update(data_buf.getData(), data_buf.getLength());
+ EXPECT_TRUE(rsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
+}
text << (readBoolean(index) ? "true" : "false");
break;
case OPT_INT8_TYPE:
- text << readInteger<int8_t>(index);
+ text << static_cast<int>(readInteger<int8_t>(index));
break;
case OPT_INT16_TYPE:
text << readInteger<int16_t>(index);
text << readInteger<int32_t>(index);
break;
case OPT_UINT8_TYPE:
- text << readInteger<uint8_t>(index);
+ text << static_cast<unsigned>(readInteger<uint8_t>(index));
break;
case OPT_UINT16_TYPE:
text << readInteger<uint16_t>(index);
// \brief Allowed clock drift (.01s)
const double DRIFT = .01;
+
+// \brief Normalized unsigned number of seconds
+uint64_t nuns(time_t t) {
+ // time_t can be a signed 32 bit integer
+ if (sizeof(time_t) == 4) {
+ // First/sign bit will be set after 20380118
+ uint32_t repr = static_cast<uint32_t>(t);
+ return (static_cast<uint64_t>(repr));
+ }
+ // or time_t is a signed 64 bit integer
+ return (static_cast<uint64_t>(t));
+}
+
+// \brief Normalized fractional seconds
+int64_t nfs(long f) {
+ // fractional seconds is a signed integer representing less than 1 second
+ return (static_cast<int64_t>(f));
+}
+
}
bool Ntp::is_zero() const {
Ntp::Ntp(const struct timeval* tv)
{
- ntp_sec_ = static_cast<uint32_t>(tv->tv_sec) + EPOCH_ADJUST;
- uint64_t fcvt = (static_cast<uint64_t>(tv->tv_usec) * 65536U) / 1000000UL;
+ ntp_sec_ = nuns(tv->tv_sec) + EPOCH_ADJUST;
+ int64_t fcvt = (nfs(static_cast<long>(tv->tv_usec)) * 65536) / 1000000;
ntp_fraction_ = static_cast<uint16_t>(fcvt & 0xffff);
}
{
ptime epoch(date(1970, Jan, 1));
time_duration dur(pt - epoch);
- ntp_sec_ = static_cast<uint32_t>(dur.total_seconds()) + EPOCH_ADJUST;
- uint64_t fcvt = static_cast<uint64_t>(dur.fractional_seconds()) * 65536U;
- fcvt /= time_duration::ticks_per_second();
+ ntp_sec_ = nuns(static_cast<unsigned>(dur.total_seconds())) + EPOCH_ADJUST;
+ int64_t fcvt = (nfs(static_cast<long>(dur.fractional_seconds())) * 65536U)
+ / time_duration::ticks_per_second();
ntp_fraction_ = static_cast<uint16_t>(fcvt & 0xffff);
}
{
double intpart;
double fracpart = std::modf(secs, &intpart);
- ntp_sec_ = static_cast<uint64_t>(intpart) + base + EPOCH_ADJUST;
+ ntp_sec_ = static_cast<uint64_t>(intpart) + nuns(base) + EPOCH_ADJUST;
ntp_fraction_ = static_cast<uint16_t>(floor(fracpart * 65536.));
}
double Ntp::secs(time_t base) const
{
double ret = static_cast<double>(ntp_sec_);
- ret -= base + EPOCH_ADJUST;
- ret += static_cast<double>(ntp_fraction_) * (1./65536.);
+ ret -= nuns(base) + EPOCH_ADJUST;
+ ret += static_cast<double>(ntp_fraction_) / 65536.;
return (ret);
}
///
/// \brief NTP (RFC 5905) time
///
+/// The \c Ntp class implements NTP timestamps
+///
/// External representation: uint64_t seconds, uint16_t fractional
/// Network representation: 48+16 bit unsigned fixed point
struct Ntp {
Ntp(const boost::posix_time::ptime pt);
// \brief Conversion from based double
+ //
+ // In place of implementing full fixed point arithmetics
+ // \c Ntp objects are converted to small floating point values.
+ // The \param base (usually set to current time) helps to keep
+ // a good accuracy.
Ntp(double secs, time_t base);
// \brief Conversion from network
run_unittests_SOURCES += memory_segment_local_unittest.cc
run_unittests_SOURCES += memory_segment_common_unittest.h
run_unittests_SOURCES += memory_segment_common_unittest.cc
+run_unittests_SOURCES += ntp_utils_unittest.cc
run_unittests_SOURCES += optional_value_unittest.cc
run_unittests_SOURCES += pid_file_unittest.cc
run_unittests_SOURCES += process_spawn_unittest.cc
--- /dev/null
+// 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 <cmath>
+#include <util/ntp_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+using namespace boost::posix_time;
+using namespace boost::gregorian;
+
+namespace {
+
+const ptime epoch(date(1970, Jan, 1));
+
+bool eq(Ntp ntpa, Ntp ntpb) {
+ return ((ntpa.ntp_sec_ == ntpb.ntp_sec_) &&
+ (ntpa.ntp_fraction_ == ntpb.ntp_fraction_));
+}
+
+}
+
+TEST(NtpUtilsTest, zero) {
+ Ntp ntp;
+ EXPECT_TRUE(ntp.is_zero());
+ ntp.ntp_fraction_ = 1;
+ EXPECT_TRUE(ntp.is_zero());
+ ntp.ntp_sec_ = 1;
+ ntp.ntp_fraction_ = 0;
+ EXPECT_FALSE(ntp.is_zero());
+}
+
+TEST(NtpUtilsTest, timeval) {
+ struct timeval tv0;
+ tv0.tv_sec = 1234;
+ tv0.tv_usec = 5665;
+ Ntp ntp0(&tv0);
+ Ntp expected0(2208990034ULL, uint16_t(371U));
+ EXPECT_TRUE(eq(ntp0, expected0));
+
+ ptime ptime1(date(2015, May, 1), hours(13) + minutes(13) + seconds(13));
+ time_duration td1(ptime1 - epoch);
+ struct timeval tv1;
+ tv1.tv_sec = td1.total_seconds();
+ tv1.tv_usec = 150;
+ Ntp ntp1(&tv1);
+ Ntp expected1(3639474793ULL, uint16_t(9U));
+ EXPECT_TRUE(eq(ntp1, expected1));
+
+ ptime ptime2(date(2045, May, 1), hours(13) + minutes(13) + seconds(13));
+ time_duration td2(ptime2 - epoch);
+ struct timeval tv2;
+ tv2.tv_sec =
+ static_cast<time_t>(static_cast<unsigned>(td2.total_seconds()));
+ tv2.tv_usec = 150;
+ Ntp ntp2(&tv2);
+ Ntp expected2(4586245993ULL, uint16_t(9U));
+ EXPECT_TRUE(eq(ntp2, expected2));
+}
+
+TEST(NtpUtilsTest, posixTime) {
+ Ntp ntp0(epoch);
+ Ntp expected0(2208988800ULL, uint16_t(0));
+ EXPECT_TRUE(eq(ntp0, expected0));
+
+ ptime ptime1(date(2015, May, 1),
+ hours(13) + minutes(13) + seconds(13) + milliseconds(150));
+ Ntp ntp1(ptime1);
+ Ntp expected1(3639474793ULL, uint16_t(9830U));
+ EXPECT_TRUE(eq(ntp1, expected1));
+
+ ptime ptime2(date(2045, May, 1),
+ hours(13) + minutes(13) + seconds(13) + milliseconds(150));
+ Ntp ntp2(ptime2);
+ Ntp expected2(4586245993ULL, uint16_t(9830U));
+ EXPECT_TRUE(eq(ntp2, expected2));
+}
+
+TEST(NtpUtilsTest, fromBinary) {
+ const uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+ vector<uint8_t> bin(8);
+ memcpy(&bin[0], data, 8);
+ vector<uint8_t> too_short = bin;
+ too_short.resize(7);
+ Ntp ntp;
+ EXPECT_FALSE(ntp.from_binary(too_short));
+ vector<uint8_t> too_long = bin;
+ too_long.push_back(0x08);
+ EXPECT_FALSE(ntp.from_binary(too_long));
+ EXPECT_TRUE(ntp.is_zero());
+ EXPECT_TRUE(ntp.from_binary(bin));
+ Ntp expected(0x10203040506ULL, uint16_t(0x708U));
+ EXPECT_TRUE(eq(ntp, expected));
+}
+
+TEST(NtpUtilsTest, toBinary) {
+ const uint8_t expected[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08 };
+ Ntp ntp(0x10203040506ULL, uint16_t(0x708U));
+ vector<uint8_t> bin(ntp.to_binary());
+ ASSERT_EQ(8, bin.size());
+ EXPECT_TRUE(memcmp(&bin[0], expected, 8) == 0);
+}
+
+TEST(NtpUtilsTest, double) {
+ struct timeval tv;
+ EXPECT_EQ(0, gettimeofday(&tv, 0));
+ Ntp ntp(&tv);
+ double d(ntp.secs(tv.tv_sec));
+ EXPECT_GE(d, 0.);
+ EXPECT_LT(d, 1.);
+
+ double d1(123.456);
+ Ntp tmp(d1, tv.tv_sec);
+ double d2(tmp.secs(tv.tv_sec));
+ EXPECT_LE(fabs(d2 - d1), 0.0001);
+}
+
+TEST(NtpUtilsTest, verifyNew) {
+ struct timeval tv;
+ EXPECT_EQ(0, gettimeofday(&tv, 0));
+ Ntp rd_new(&tv);
+ Ntp ts_new(&tv);
+ EXPECT_TRUE(Ntp::verify_new(rd_new, ts_new));
+ ts_new.ntp_sec_ -= 200;
+ EXPECT_TRUE(Ntp::verify_new(rd_new,ts_new));
+ ts_new.ntp_sec_ -= 200;
+ EXPECT_FALSE(Ntp::verify_new(rd_new,ts_new));
+ ts_new = rd_new;
+ ts_new.ntp_sec_ += 200;
+ EXPECT_TRUE(Ntp::verify_new(rd_new,ts_new));
+ ts_new.ntp_sec_ += 200;
+ EXPECT_FALSE(Ntp::verify_new(rd_new,ts_new));
+}
+
+TEST(NtpUtilsTest, verify) {
+ struct timeval tv;
+ EXPECT_EQ(0, gettimeofday(&tv, 0));
+ Ntp rd_new(&tv);
+ Ntp ts_new(&tv);
+ Ntp rd_last(&tv);
+ rd_last.ntp_sec_ -= 1000;
+ Ntp ts_last(&tv);
+ ts_last.ntp_sec_ -= 1000;
+ bool to_update0 = false;
+ EXPECT_TRUE(Ntp::verify(rd_new, ts_new, rd_last, ts_last, &to_update0));
+ EXPECT_TRUE(to_update0);
+ bool to_update1 = false;
+ Ntp next1(&tv);
+ next1.ntp_sec_ += 1;
+ EXPECT_TRUE(Ntp::verify(rd_new, ts_new, rd_new, next1, &to_update1));
+ EXPECT_FALSE(to_update1);
+ bool to_update2 = false;
+ EXPECT_FALSE(Ntp::verify(rd_new, ts_new, next1, next1, &to_update2));
+ EXPECT_FALSE(to_update2);
+ ts_new.ntp_sec_ -= 10;
+ bool to_update3 = false;
+ EXPECT_TRUE(Ntp::verify(rd_new, ts_new, rd_last, ts_last, &to_update3));
+ EXPECT_TRUE(to_update3);
+ ts_new.ntp_sec_ -= 10;
+ bool to_update4 = false;
+ EXPECT_FALSE(Ntp::verify(rd_new, ts_new, rd_last, ts_last, &to_update4));
+ EXPECT_FALSE(to_update4);
+}