--- a/build.sh
+++ b/build.sh
@@ -20,16 +20,17 @@ if [ -n "$CCC" ] && [ -z "$CXX" ]; then
fi
while [ $# -gt 0 ]; do
case $1 in
-c) CLEAN=1 ;;
-g) REBUILD_GYP=1 ;;
-v) VERBOSE=1 ;;
--test) GYP_PARAMS="$GYP_PARAMS -Dtest_build=1" ;;
+ --fuzz) GYP_PARAMS="$GYP_PARAMS -Dtest_build=1 -Dfuzz=1" ;;
esac
shift
done
# -c = clean first
if [ "$CLEAN" = 1 ]; then
rm -rf "$CWD/out"
fi
--- a/coreconf/config.gypi
+++ b/coreconf/config.gypi
@@ -47,16 +47,21 @@
}],
['OS=="linux" or OS=="android"', {
'zlib_libs%': ['<!@(<(python) <(DEPTH)/coreconf/pkg_config.py raw --libs zlib)'],
'moz_debug_flags%': '-gdwarf-2',
'optimize_flags%': '-O2',
'dll_prefix': 'lib',
'dll_suffix': 'so',
}],
+ ['OS=="linux"', {
+ 'freebl_name': 'freeblpriv3',
+ }, {
+ 'freebl_name': 'freebl3',
+ }],
['OS=="mac"', {
'zlib_libs%': ['-lz'],
'use_system_sqlite%': 1,
'moz_debug_flags%': '-gdwarf-2 -gfull',
'optimize_flags%': '-O2',
'dll_prefix': 'lib',
'dll_suffix': 'dylib',
}, {
@@ -80,29 +85,31 @@
'nspr_lib_dir%': '<(nspr_lib_dir)',
'nspr_include_dir%': '<(nspr_include_dir)',
'nss_dist_obj_dir%': '<(nss_dist_obj_dir)',
'nss_dist_dir%': '<(nss_dist_dir)',
'use_system_sqlite%': '<(use_system_sqlite)',
'sqlite_libs%': ['-lsqlite3'],
'dll_prefix': '<(dll_prefix)',
'dll_suffix': '<(dll_suffix)',
+ 'freebl_name': '<(freebl_name)',
'cc_is_clang%': '<(cc_is_clang)',
# Some defaults
'disable_tests%': 0,
'disable_chachapoly%': 0,
'disable_dbm%': 0,
'disable_libpkix%': 0,
'disable_werror%': 0,
'mozilla_client%': 0,
'moz_fold_libs%': 0,
'moz_folded_library_name%': '',
'ssl_enable_zlib%': 1,
'use_asan%': 0,
'test_build%': 0,
+ 'fuzz%': 0,
},
'target_defaults': {
# Settings specific to targets should go here.
# This is mostly for linking to libraries.
'variables': {
'mapfile%': '',
'test_build%': 0,
},
@@ -274,16 +281,19 @@
}],
],
}],
[ 'disable_werror==0 and (OS=="linux" or OS=="mac")', {
'cflags': [
'<!@(<(python) <(DEPTH)/coreconf/werror.py)',
],
}],
+ [ 'fuzz==1', {
+ 'cflags': ['-Wno-unused-function']
+ }],
[ 'OS=="android" and mozilla_client==0', {
'defines': [
'NO_SYSINFO',
'NO_FORK_CHECK',
'ANDROID',
],
}],
[ 'OS=="mac"', {
--- a/external_tests/common/gtest.gypi
+++ b/external_tests/common/gtest.gypi
@@ -12,16 +12,21 @@
'-lws2_32',
],
}],
['OS=="android"', {
'libraries': [
'-lstdc++',
],
}],
+ [ 'fuzz==1', {
+ 'defines': [
+ 'UNSAFE_FUZZER_MODE',
+ ],
+ }],
],
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1,
'PreprocessorDefinitions': [
'NOMINMAX',
],
},
--- a/external_tests/pk11_gtest/manifest.mn
+++ b/external_tests/pk11_gtest/manifest.mn
@@ -7,16 +7,17 @@ DEPTH = ../..
MODULE = nss
CPPSRCS = \
pk11_aeskeywrap_unittest.cc \
pk11_chacha20poly1305_unittest.cc \
pk11_export_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
+ pk11_prng_unittest.cc \
pk11_rsapss_unittest.cc \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/external_tests/google_test/gtest/include \
-I$(CORE_DEPTH)/external_tests/common
REQUIRES = nspr nss libdbm gtest
--- a/external_tests/pk11_gtest/pk11_gtest.gyp
+++ b/external_tests/pk11_gtest/pk11_gtest.gyp
@@ -10,28 +10,25 @@
{
'target_name': 'pk11_gtest',
'type': 'executable',
'sources': [
'pk11_aeskeywrap_unittest.cc',
'pk11_chacha20poly1305_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
+ 'pk11_prng_unittest.cc',
'pk11_rsapss_unittest.cc',
'<(DEPTH)/external_tests/common/gtests.cc'
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
- '<(DEPTH)/lib/nss/nss.gyp:nss3',
- '<(DEPTH)/lib/util/util.gyp:nssutil3',
- '<(DEPTH)/lib/smime/smime.gyp:smime3',
- '<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
+ '<(DEPTH)/lib/freebl/freebl.gyp:<(freebl_name)',
'<(DEPTH)/external_tests/google_test/google_test.gyp:gtest',
- '<(DEPTH)/cmd/lib/lib.gyp:sectool'
- ]
+ ],
}
],
'target_defaults': {
'include_dirs': [
'../../external_tests/google_test/gtest/include',
'../../external_tests/common'
]
},
new file mode 100644
--- /dev/null
+++ b/external_tests/pk11_gtest/pk11_prng_unittest.cc
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 <memory>
+#include "blapi.h"
+#include "pk11pub.h"
+
+#include "gtest/gtest.h"
+
+namespace nss_test {
+
+class PK11PrngTest : public ::testing::Test {};
+
+#ifdef UNSAFE_FUZZER_MODE
+
+// Test that two consecutive calls to the RNG return two distinct values.
+TEST_F(PK11PrngTest, Fuzz_DetPRNG) {
+ std::vector<uint8_t> rnd1(2048, 0);
+ std::vector<uint8_t> rnd2(2048, 0);
+
+ SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
+ EXPECT_EQ(rv, SECSuccess);
+
+ rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
+ EXPECT_EQ(rv, SECSuccess);
+
+ EXPECT_NE(rnd1, rnd2);
+}
+
+// Test that two consecutive calls to the RNG return two equal values
+// when the RNG's internal state is reset before each call.
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_Reset) {
+ std::vector<uint8_t> rnd1(2048, 0);
+ std::vector<uint8_t> rnd2(2048, 0);
+
+ RNG_ResetForFuzzing();
+
+ SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
+ EXPECT_EQ(rv, SECSuccess);
+
+ RNG_ResetForFuzzing();
+
+ rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
+ EXPECT_EQ(rv, SECSuccess);
+
+ EXPECT_EQ(rnd1, rnd2);
+}
+
+// Test that the RNG's internal state progresses in a consistent manner.
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_StatefulReset) {
+ std::vector<uint8_t> rnd1(2048, 0);
+ std::vector<uint8_t> rnd2(2048, 0);
+
+ RNG_ResetForFuzzing();
+
+ SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size() - 1024);
+ EXPECT_EQ(rv, SECSuccess);
+
+ rv = PK11_GenerateRandom(rnd1.data() + 1024, rnd1.size() - 1024);
+ EXPECT_EQ(rv, SECSuccess);
+
+ RNG_ResetForFuzzing();
+
+ rv = PK11_GenerateRandom(rnd2.data(), rnd2.size() - 1024);
+ EXPECT_EQ(rv, SECSuccess);
+
+ rv = PK11_GenerateRandom(rnd2.data() + 1024, rnd2.size() - 1024);
+ EXPECT_EQ(rv, SECSuccess);
+
+ EXPECT_EQ(rnd1, rnd2);
+}
+
+#endif
+
+} // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/fuzz/warning.txt
@@ -0,0 +1,15 @@
+
+##############################################
+## ##
+## WARNING: You're building with -Dfuzz=1 ##
+## ##
+## This means: ##
+## ##
+## * Your PRNG is DETERMINISTIC. ##
+## * TLS transcripts are PLAINTEXT. ##
+## * TLS signature checks are DISABLED. ##
+## ##
+## Thank you for fuzzing! ##
+## ##
+##############################################
+
--- a/lib/freebl/blapi.h
+++ b/lib/freebl/blapi.h
@@ -1424,16 +1424,18 @@ extern SECStatus RNG_RNGInit(void);
extern SECStatus RNG_RandomUpdate(const void *data, size_t bytes);
/*
** Generate some random bytes, using the global random number generator
** object.
*/
extern SECStatus RNG_GenerateGlobalRandomBytes(void *dest, size_t len);
+extern SECStatus RNG_ResetForFuzzing(void);
+
/* Destroy the global RNG context. After a call to RNG_RNGShutdown()
** a call to RNG_RNGInit() is required in order to use the generator again,
** along with seed data (see the comment above RNG_RNGInit()).
*/
extern void RNG_RNGShutdown(void);
extern void RNG_SystemInfoForRNG(void);
new file mode 100644
--- /dev/null
+++ b/lib/freebl/det_rng.c
@@ -0,0 +1,67 @@
+/* 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 "blapi.h"
+#include "blapit.h"
+#include "chacha20.h"
+#include "nssilock.h"
+#include "seccomon.h"
+#include "secerr.h"
+
+static unsigned long globalNumCalls = 0;
+
+SECStatus
+prng_ResetForFuzzing(PZLock *rng_lock)
+{
+ /* Check for a valid RNG lock. */
+ PORT_Assert(rng_lock != NULL);
+ if (rng_lock == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* --- LOCKED --- */
+ PZ_Lock(rng_lock);
+ globalNumCalls = 0;
+ PZ_Unlock(rng_lock);
+ /* --- UNLOCKED --- */
+
+ return SECSuccess;
+}
+
+SECStatus
+prng_GenerateDeterministicRandomBytes(PZLock *rng_lock, void *dest, size_t len)
+{
+ static const uint8_t key[32];
+ uint8_t nonce[12] = { 0 };
+
+ /* Check for a valid RNG lock. */
+ PORT_Assert(rng_lock != NULL);
+ if (rng_lock == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* --- LOCKED --- */
+ PZ_Lock(rng_lock);
+
+ memcpy(nonce, &globalNumCalls, sizeof(globalNumCalls));
+ globalNumCalls++;
+
+ ChaCha20Poly1305Context *cx =
+ ChaCha20Poly1305_CreateContext(key, sizeof(key), 16);
+ if (!cx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PZ_Unlock(rng_lock);
+ return SECFailure;
+ }
+
+ memset(dest, 0, len);
+ ChaCha20XOR(dest, dest, len, key, nonce, 0);
+ ChaCha20Poly1305_DestroyContext(cx, PR_TRUE);
+
+ PZ_Unlock(rng_lock);
+ /* --- UNLOCKED --- */
+ return SECSuccess;
+}
new file mode 100644
--- /dev/null
+++ b/lib/freebl/det_rng.h
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#ifndef __det_rng_h_
+#define __det_rng_h_
+
+SECStatus prng_ResetForFuzzing(PZLock *rng_lock);
+SECStatus prng_GenerateDeterministicRandomBytes(PZLock *rng_lock, void *dest,
+ size_t len);
+
+#endif /* __det_rng_h_ */
--- a/lib/freebl/drbg.c
+++ b/lib/freebl/drbg.c
@@ -15,16 +15,20 @@
#include "blapii.h"
#include "nssilock.h"
#include "secitem.h"
#include "sha_fast.h"
#include "sha256.h"
#include "secrng.h" /* for RNG_SystemRNG() */
#include "secmpi.h"
+#ifdef UNSAFE_FUZZER_MODE
+#include "det_rng.h"
+#endif
+
/* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1
* for SHA-1, SHA-224, and SHA-256 it's 440 bits.
* for SHA-384 and SHA-512 it's 888 bits */
#define PRNG_SEEDLEN (440 / PR_BITS_PER_BYTE)
#define PRNG_MAX_ADDITIONAL_BYTES PR_INT64(0x100000000)
/* 2^35 bits or 2^32 bytes */
#define PRNG_MAX_REQUEST_SIZE 0x10000 /* 2^19 bits or 2^16 bytes */
#define PRNG_ADDITONAL_DATA_CACHE_SIZE (8 * 1024) /* must be less than \
@@ -645,17 +649,31 @@ prng_GenerateGlobalRandomBytes(RNGContex
/*
** Generate some random bytes, using the global random number generator
** object.
*/
SECStatus
RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
{
+#ifdef UNSAFE_FUZZER_MODE
+ return prng_GenerateDeterministicRandomBytes(globalrng->lock, dest, len);
+#else
return prng_GenerateGlobalRandomBytes(globalrng, dest, len);
+#endif
+}
+
+SECStatus
+RNG_ResetForFuzzing(void)
+{
+#ifdef UNSAFE_FUZZER_MODE
+ return prng_ResetForFuzzing(globalrng->lock);
+#else
+ return SECFailure;
+#endif
}
void
RNG_RNGShutdown(void)
{
/* check for a valid global RNG context */
PORT_Assert(globalrng != NULL);
if (globalrng == NULL) {
--- a/lib/freebl/freebl.gyp
+++ b/lib/freebl/freebl.gyp
@@ -42,16 +42,17 @@
'arcfive.c',
'arcfour.c',
'camellia.c',
'chacha20poly1305.c',
'ctr.c',
'cts.c',
'des.c',
'desblapi.c',
+ 'det_rng.c',
'dh.c',
'drbg.c',
'dsa.c',
'ec.c',
'ecdecode.c',
'ecl/ec_naf.c',
'ecl/ecl.c',
'ecl/ecl_curve.c',
@@ -208,16 +209,21 @@
# not x64
'sources': [
'chacha20.c',
'poly1305.c',
],
}],
],
}],
+ [ 'fuzz==1', {
+ 'defines': [
+ 'UNSAFE_FUZZER_MODE',
+ ],
+ }],
[ 'OS=="mac"', {
'conditions': [
[ 'target_arch=="ia32"', {
'sources': [
'mpi/mpi_sse2.s',
],
'defines': [
'MP_USE_UINT_DIGIT',
@@ -388,17 +394,10 @@
[ 'OS=="mac"', {
}],
[ 'OS=="win"', {
}],
],
},
'variables': {
'module': 'nss',
- 'conditions': [
- [ 'OS=="linux"', {
- 'freebl_name': 'freeblpriv3',
- }, {
- 'freebl_name': 'freebl3',
- }],
- ],
}
}
--- a/nss.gyp
+++ b/nss.gyp
@@ -225,10 +225,27 @@
'dependencies': [
'cmd/pkix-errcodes/pkix-errcodes.gyp:pkix-errcodes',
],
}],
],
},
],
}],
+ [ 'fuzz==1', {
+ 'targets': [
+ {
+ 'target_name': 'fuzz',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'warn_fuzz',
+ 'action': ['cat', 'fuzz/warning.txt'],
+ 'inputs': ['fuzz/warning.txt'],
+ 'ninja_use_console': 1,
+ 'outputs': ['dummy'],
+ }
+ ],
+ },
+ ],
+ }],
],
}