* CID 1341682: Nit in libntp/authreadkeys.c. HStenn.
* CID 1341684: Nit in tests/ntpd/t-ntp_signd.c. HStenn.
* [Bug 2829] Look at pipe_fds in ntpd.c (did so. perlinger@ntp.org)
+* [Bug 2879] Improve NTP security against timing attacks. perlinger@ntp.org
+ - integrated patches by Loganaden Velvidron <loganaden@gmail.com>
+ with some modifications & unit tests
* [Bug 2887] stratum -1 config results as showing value 99
- fudge stratum only accepts values [0..16]. perlinger@ntp.org
* [Bug 2932] Update leapsecond file info in miscopt.html. CWoodbury, HStenn.
REQUIRE(len <= ISC_MD5_DIGESTLENGTH);
isc_hmacmd5_sign(ctx, newdigest);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
REQUIRE(len <= ISC_SHA1_DIGESTLENGTH);
isc_hmacsha1_sign(ctx, newdigest, ISC_SHA1_DIGESTLENGTH);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
/*
REQUIRE(len <= ISC_SHA224_DIGESTLENGTH);
isc_hmacsha224_sign(ctx, newdigest, ISC_SHA224_DIGESTLENGTH);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
/*
REQUIRE(len <= ISC_SHA256_DIGESTLENGTH);
isc_hmacsha256_sign(ctx, newdigest, ISC_SHA256_DIGESTLENGTH);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
/*
REQUIRE(len <= ISC_SHA384_DIGESTLENGTH);
isc_hmacsha384_sign(ctx, newdigest, ISC_SHA384_DIGESTLENGTH);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
/*
REQUIRE(len <= ISC_SHA512_DIGESTLENGTH);
isc_hmacsha512_sign(ctx, newdigest, ISC_SHA512_DIGESTLENGTH);
- return (ISC_TF(memcmp(digest, newdigest, len) == 0));
+ return (ISC_TF(isc_tsmemcmp(digest, newdigest, len) == 0));
}
*
*/
+int
+isc_tsmemcmp(const void *p1, const void *p2, size_t len);
+/*
+ * Lexicographic compare 'len' unsigned bytes from 'p1' and 'p2'
+ * like 'memcmp()'.
+ *
+ * This function is safe from timing attacks as it has a runtime that
+ * only depends on 'len' and has no early-out option.
+ *
+ * Use this to check MACs and other material that is security sensitive.
+ *
+ * Returns:
+ * (let x be the byte offset of the first different byte)
+ * -1 if (u_char)p1[x] < (u_char)p2[x]
+ * 1 if (u_char)p1[x] > (u_char)p2[x]
+ * 0 if byte series are equal
+ */
+
char *
isc_string_separate(char **stringp, const char *delim);
--- /dev/null
+/*
+ * Copyright (C) 2004-2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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.
+ */
+
+/* $Id$ */
+
+/*! \file */
+
+#include <config.h>
+#include <limits.h>
+#include <isc/string.h>
+
+/* Making a portable memcmp that has no internal branches and loops always
+ * once for every byte without early-out shortcut has a few challenges.
+ *
+ * Inspired by 'timingsafe_memcmp()' from the BSD system and
+ * https://github.com/libressl-portable/openbsd/blob/master/src/lib/libc/string/timingsafe_memcmp.c
+ *
+ * Sadly, that one is not portable C: It makes assumptions on the representation
+ * of negative integers and assumes sign-preserving right-shift of negative
+ * signed values. This is a rewrite from scratch that should not suffer from
+ * such issues.
+ *
+ * 2015-12-12, J. Perlinger (perlinger-at-ntp-dot-org)
+ */
+int
+isc_tsmemcmp(const void *p1, const void *p2, size_t nb) {
+ const unsigned char *ucp1 = p1;
+ const unsigned char *ucp2 = p2;
+ unsigned int isLT = 0u;
+ unsigned int isGT = 0u;
+ volatile unsigned int mask = (1u << CHAR_BIT);
+
+ for (/*NOP*/; 0 != nb; --nb, ++ucp1, ++ucp2) {
+ isLT |= mask &
+ ((unsigned int)*ucp1 - (unsigned int)*ucp2);
+ isGT |= mask &
+ ((unsigned int)*ucp2 - (unsigned int)*ucp1);
+ mask &= ~(isLT | isGT);
+ }
+ return (int)(isGT >> CHAR_BIT) - (int)(isLT >> CHAR_BIT);
+}
$(srcdir)/../lib/isc/unix/time.c \
$(srcdir)/../lib/isc/sha1.c \
$(srcdir)/../lib/isc/sockaddr.c \
+ $(srcdir)/../lib/isc/tsmemcmp.c \
$(NULL)
if PTHREADS
#include "ntp_stdlib.h"
#include "ntp.h"
#include "ntp_md5.h" /* provides OpenSSL digest API */
-
+#include "isc/string.h"
/*
* MD5authencrypt - generate message digest
*
"MAC decrypt: MAC length error");
return (0);
}
- return !memcmp(digest, (const char *)pkt + length + 4, len);
+ return !isc_tsmemcmp(digest, (const char *)pkt + length + 4, len);
}
/*
RelativePath="..\..\..\libntp\timevalops.c"
>
</File>
+ <File
+ RelativePath="..\..\..\lib\isc\tsmemcmp.c"
+ >
+ </File>
<File
RelativePath="..\..\..\libntp\uglydate.c"
>
RelativePath="..\..\..\..\libntp\timevalops.c"
>
</File>
+ <File
+ RelativePath="..\..\..\..\lib\isc\tsmemcmp.c"
+ >
+ </File>
<File
RelativePath="..\..\..\..\libntp\uglydate.c"
>
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugXP|Win32">
<ClCompile Include="..\..\..\..\lib\isc\sha1.c" />
<ClCompile Include="..\..\..\..\lib\isc\sockaddr.c" />
<ClCompile Include="..\..\..\..\lib\isc\task.c" />
+ <ClCompile Include="..\..\..\..\lib\isc\tsmemcmp.c" />
<ClCompile Include="..\..\..\..\lib\isc\win32\condition.c" />
<ClCompile Include="..\..\..\..\lib\isc\win32\interfaceiter.c" />
<ClCompile Include="..\..\..\..\lib\isc\win32\net.c" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<ClCompile Include="..\..\..\..\libntp\timevalops.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\libntp\tsmemcmp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\libntp\uglydate.c">
<Filter>Source Files</Filter>
</ClCompile>
<Filter>Resource Files</Filter>
</CustomBuild>
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
#include <config.h>
#include "crypto.h"
#include <ctype.h>
+#include "isc/string.h"
struct key *key_ptr;
size_t key_cnt = 0;
if (!hash_len)
authentic = FALSE;
else
- authentic = !memcmp(digest, pkt_data + pkt_size + 4,
+ authentic = !isc_tsmemcmp(digest, pkt_data + pkt_size + 4,
hash_len);
return authentic;
}
test-strtolfp \
test-timespecops \
test-timevalops \
+ test-tsafememcmp \
test-tstotv \
test-tvtots \
test-uglydate \
$(srcdir)/run-strtolfp.c \
$(srcdir)/run-timevalops.c \
$(srcdir)/run-timespecops.c \
+ $(srcdir)/run-tsafememcmp.c \
$(srcdir)/run-tstotv.c \
$(srcdir)/run-tvtots.c \
$(srcdir)/run-uglydate.c \
###
+test_tsafememcmp_SOURCES = \
+ tsafememcmp.c \
+ run-tsafememcmp.c \
+ $(NULL)
+
+$(srcdir)/run-tsafememcmp.c: $(srcdir)/tsafememcmp.c $(std_unity_list)
+ $(run_unity) tsafememcmp.c run-tsafememcmp.c
+
+###
+
test_tstotv_SOURCES = \
tstotv.c \
run-tstotv.c \
--- /dev/null
+/* AUTOGENERATED FILE. DO NOT EDIT. */
+
+//=======Test Runner Used To Run Each Test Below=====
+#define RUN_TEST(TestFunc, TestLineNum) \
+{ \
+ Unity.CurrentTestName = #TestFunc; \
+ Unity.CurrentTestLineNumber = TestLineNum; \
+ Unity.NumberOfTests++; \
+ if (TEST_PROTECT()) \
+ { \
+ setUp(); \
+ TestFunc(); \
+ } \
+ if (TEST_PROTECT() && !TEST_IS_IGNORED) \
+ { \
+ tearDown(); \
+ } \
+ UnityConcludeTest(); \
+}
+
+//=======Automagically Detected Files To Include=====
+#include "unity.h"
+#include <setjmp.h>
+#include <stdio.h>
+#include "config.h"
+#include "ntp_stdlib.h"
+#include "isc/string.h"
+
+//=======External Functions This Runner Calls=====
+extern void setUp(void);
+extern void tearDown(void);
+extern void test_Empty(void);
+extern void test_Equal(void);
+extern void test_FirstByte(void);
+extern void test_LastByte(void);
+extern void test_MiddleByte(void);
+extern void test_MiddleByteUpLo(void);
+
+
+//=======Test Reset Option=====
+void resetTest(void);
+void resetTest(void)
+{
+ tearDown();
+ setUp();
+}
+
+char const *progname;
+
+
+//=======MAIN=====
+int main(int argc, char *argv[])
+{
+ progname = argv[0];
+ UnityBegin("tsafememcmp.c");
+ RUN_TEST(test_Empty, 10);
+ RUN_TEST(test_Equal, 11);
+ RUN_TEST(test_FirstByte, 12);
+ RUN_TEST(test_LastByte, 13);
+ RUN_TEST(test_MiddleByte, 14);
+ RUN_TEST(test_MiddleByteUpLo, 15);
+
+ return (UnityEnd());
+}
--- /dev/null
+#include "config.h"
+
+#include "ntp_stdlib.h"
+#include "isc/string.h"
+
+#include "unity.h"
+
+/* Basisc test for timingsafe_memcmp() */
+
+void test_Empty(void);
+void test_Equal(void);
+void test_FirstByte(void);
+void test_LastByte(void);
+void test_MiddleByte(void);
+void test_MiddleByteUpLo(void);
+
+void test_Empty(void)
+{
+ static const char dummy[1];
+ TEST_ASSERT_EQUAL_INT(0, isc_tsmemcmp(NULL , NULL , 0));
+ TEST_ASSERT_EQUAL_INT(0, isc_tsmemcmp(dummy, dummy, 0));
+}
+
+void test_Equal(void)
+{
+ static const char dummy[2][4] = {
+ "blob", "blob"
+ };
+ TEST_ASSERT_EQUAL_INT(0, isc_tsmemcmp(dummy[0],
+ dummy[1],
+ sizeof(dummy[0])));
+}
+
+void test_FirstByte(void)
+{
+ static const char dummy[2][4] = {
+ "Blob", "Clob"
+ };
+ TEST_ASSERT_EQUAL_INT(-1, isc_tsmemcmp(dummy[0],
+ dummy[1],
+ sizeof(dummy[0])));
+ TEST_ASSERT_EQUAL_INT( 1, isc_tsmemcmp(dummy[1],
+ dummy[0],
+ sizeof(dummy[0])));
+}
+
+void test_LastByte(void)
+{
+ static const char dummy[2][4] = {
+ "Blob", "Bloc"
+ };
+ TEST_ASSERT_EQUAL_INT(-1, isc_tsmemcmp(dummy[0],
+ dummy[1],
+ sizeof(dummy[0])));
+ TEST_ASSERT_EQUAL_INT( 1, isc_tsmemcmp(dummy[1],
+ dummy[0],
+ sizeof(dummy[0])));
+}
+
+void test_MiddleByte(void)
+{
+ static const char dummy[2][4] = {
+ "Blob", "Blpb"
+ };
+ TEST_ASSERT_EQUAL_INT(-1, isc_tsmemcmp(dummy[0],
+ dummy[1],
+ sizeof(dummy[0])));
+ TEST_ASSERT_EQUAL_INT( 1, isc_tsmemcmp(dummy[1],
+ dummy[0],
+ sizeof(dummy[0])));
+}
+
+void test_MiddleByteUpLo(void)
+{
+ static const char dummy[2][4] = {
+ "Blob", "Blpa"
+ };
+ TEST_ASSERT_EQUAL_INT(-1, isc_tsmemcmp(dummy[0],
+ dummy[1],
+ sizeof(dummy[0])));
+ TEST_ASSERT_EQUAL_INT( 1, isc_tsmemcmp(dummy[1],
+ dummy[0],
+ sizeof(dummy[0])));
+}
+