Skip to content

Commit d535b90

Browse files
authored
Adding a FuzzTest for fuzzing PASE (project-chip#36171)
1 parent 8211b69 commit d535b90

File tree

3 files changed

+329
-0
lines changed

3 files changed

+329
-0
lines changed

BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") {
7070
"${chip_root}/src/lib/core/tests:fuzz-tlv-reader-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
7171
"${chip_root}/src/lib/dnssd/minimal_mdns/tests:fuzz-minmdns-packet-parsing-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
7272
"${chip_root}/src/lib/format/tests:fuzz-payload-decoder-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
73+
"${chip_root}/src/protocols/secure_channel/tests:fuzz-PASE-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
7374
"${chip_root}/src/setup_payload/tests:fuzz-setup-payload-base38-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
7475
]
7576
}

src/protocols/secure_channel/tests/BUILD.gn

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import("//build_overrides/build.gni")
22
import("//build_overrides/chip.gni")
3+
34
import("${chip_root}/build/chip/chip_test_suite.gni")
5+
import("${chip_root}/build/chip/fuzz_test.gni")
46
import("${chip_root}/src/app/icd/icd.gni")
57

68
chip_test_suite("tests") {
@@ -45,3 +47,25 @@ chip_test_suite("tests") {
4547
public_deps += [ "${chip_root}/src/app/icd/server:configuration-data" ]
4648
}
4749
}
50+
if (pw_enable_fuzz_test_targets) {
51+
chip_pw_fuzz_target("fuzz-PASE-pw") {
52+
test_source = [ "FuzzPASE_PW.cpp" ]
53+
public_deps = [
54+
"${chip_root}/src/app/icd/server:icd-server-config",
55+
"${chip_root}/src/credentials/tests:cert_test_vectors",
56+
"${chip_root}/src/crypto/tests:tests.lib",
57+
"${chip_root}/src/lib/core",
58+
"${chip_root}/src/lib/core:string-builder-adapters",
59+
"${chip_root}/src/lib/support",
60+
"${chip_root}/src/lib/support:test_utils",
61+
"${chip_root}/src/lib/support:testing",
62+
"${chip_root}/src/lib/support/tests:pw-test-macros",
63+
"${chip_root}/src/messaging/tests:helpers",
64+
"${chip_root}/src/protocols",
65+
"${chip_root}/src/protocols/secure_channel",
66+
"${chip_root}/src/protocols/secure_channel:check-in-counter",
67+
"${chip_root}/src/transport/raw/tests:helpers",
68+
"${dir_pw_unit_test}",
69+
]
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
#include <cstddef>
2+
#include <cstdint>
3+
#include <stdarg.h>
4+
#include <string.h>
5+
6+
#include <pw_fuzzer/fuzztest.h>
7+
#include <pw_unit_test/framework.h>
8+
9+
#include <app/icd/server/ICDServerConfig.h>
10+
#include <lib/core/CHIPCore.h>
11+
#include <lib/core/CHIPSafeCasts.h>
12+
#include <lib/core/StringBuilderAdapters.h>
13+
#include <lib/support/CHIPMem.h>
14+
#include <lib/support/CodeUtils.h>
15+
#include <lib/support/UnitTestUtils.h>
16+
#include <messaging/tests/MessagingContext.h>
17+
#include <protocols/secure_channel/PASESession.h>
18+
19+
namespace {
20+
21+
using namespace chip;
22+
using namespace std;
23+
24+
using namespace chip::Crypto;
25+
using namespace fuzztest;
26+
using namespace chip::Transport;
27+
using namespace chip::Messaging;
28+
using namespace System::Clock::Literals;
29+
30+
// TODO: #35369 Refactor the classes below to Fixtures once Errors related to FuzzTest Fixtures are resolved
31+
class FuzzLoopbackMessagingContext : public chip::Test::MessagingContext
32+
{
33+
public:
34+
~FuzzLoopbackMessagingContext() {}
35+
36+
// These functions wrap spLoopbackTransportManager methods
37+
static auto & GetSystemLayer() { return spLoopbackTransportManager->GetSystemLayer(); }
38+
static auto & GetLoopback() { return spLoopbackTransportManager->GetLoopback(); }
39+
static auto & GetTransportMgr() { return spLoopbackTransportManager->GetTransportMgr(); }
40+
static auto & GetIOContext() { return spLoopbackTransportManager->GetIOContext(); }
41+
42+
template <typename... Ts>
43+
static void DrainAndServiceIO(Ts... args)
44+
{
45+
return spLoopbackTransportManager->DrainAndServiceIO(args...);
46+
}
47+
48+
// Performs shared setup for all tests in the test suite
49+
static void SetUpTestSuite()
50+
{
51+
// Initialize memory.
52+
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
53+
// Instantiate the LoopbackTransportManager.
54+
ASSERT_EQ(spLoopbackTransportManager, nullptr);
55+
spLoopbackTransportManager = new chip::Test::LoopbackTransportManager();
56+
ASSERT_NE(spLoopbackTransportManager, nullptr);
57+
// Initialize the LoopbackTransportManager.
58+
ASSERT_EQ(spLoopbackTransportManager->Init(), CHIP_NO_ERROR);
59+
}
60+
61+
// Performs shared teardown for all tests in the test suite
62+
static void TearDownTestSuite()
63+
{
64+
// Shutdown the LoopbackTransportManager.
65+
spLoopbackTransportManager->Shutdown();
66+
// Destroy the LoopbackTransportManager.
67+
if (spLoopbackTransportManager != nullptr)
68+
{
69+
delete spLoopbackTransportManager;
70+
spLoopbackTransportManager = nullptr;
71+
}
72+
// Shutdown memory.
73+
chip::Platform::MemoryShutdown();
74+
}
75+
76+
// Performs setup for each individual test in the test suite
77+
void SetUp() { ASSERT_EQ(MessagingContext::Init(&GetTransportMgr(), &GetIOContext()), CHIP_NO_ERROR); }
78+
79+
// Performs teardown for each individual test in the test suite
80+
void TearDown() { MessagingContext::Shutdown(); }
81+
82+
static chip::Test::LoopbackTransportManager * spLoopbackTransportManager;
83+
};
84+
chip::Test::LoopbackTransportManager * FuzzLoopbackMessagingContext::spLoopbackTransportManager = nullptr;
85+
86+
class TestSecurePairingDelegate : public SessionEstablishmentDelegate
87+
{
88+
public:
89+
void OnSessionEstablishmentError(CHIP_ERROR error) override { mNumPairingErrors++; }
90+
91+
void OnSessionEstablished(const SessionHandle & session) override { mNumPairingComplete++; }
92+
93+
uint32_t mNumPairingErrors = 0;
94+
uint32_t mNumPairingComplete = 0;
95+
};
96+
97+
class TestPASESession : public FuzzLoopbackMessagingContext
98+
{
99+
public:
100+
TestPASESession()
101+
{
102+
ConfigInitializeNodes(false);
103+
FuzzLoopbackMessagingContext::SetUpTestSuite();
104+
FuzzLoopbackMessagingContext::SetUp();
105+
}
106+
~TestPASESession()
107+
{
108+
FuzzLoopbackMessagingContext::TearDown();
109+
FuzzLoopbackMessagingContext::TearDownTestSuite();
110+
}
111+
112+
void SecurePairingHandshake(SessionManager & sessionManager, PASESession & pairingCommissioner,
113+
TestSecurePairingDelegate & delegateCommissioner, TestSecurePairingDelegate & delegateAccessory,
114+
const Spake2pVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt,
115+
uint32_t SetUpPINCode);
116+
};
117+
118+
class TemporarySessionManager
119+
{
120+
public:
121+
TemporarySessionManager(TestPASESession & ctx) : mCtx(ctx)
122+
{
123+
EXPECT_EQ(CHIP_NO_ERROR,
124+
mSessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &ctx.GetMessageCounterManager(), &mStorage,
125+
&ctx.GetFabricTable(), ctx.GetSessionKeystore()));
126+
// The setup here is really weird: we are using one session manager for
127+
// the actual messages we send (the PASE handshake, so the
128+
// unauthenticated sessions) and a different one for allocating the PASE
129+
// sessions. Since our Init() set us up as the thing to handle messages
130+
// on the transport manager, undo that.
131+
mCtx.GetTransportMgr().SetSessionManager(&mCtx.GetSecureSessionManager());
132+
}
133+
134+
~TemporarySessionManager()
135+
{
136+
mSessionManager.Shutdown();
137+
// Reset the session manager on the transport again, just in case
138+
// shutdown messed with it.
139+
mCtx.GetTransportMgr().SetSessionManager(&mCtx.GetSecureSessionManager());
140+
}
141+
142+
operator SessionManager &() { return mSessionManager; }
143+
144+
private:
145+
TestPASESession & mCtx;
146+
TestPersistentStorageDelegate mStorage;
147+
SessionManager mSessionManager;
148+
};
149+
150+
class PASETestLoopbackTransportDelegate : public Test::LoopbackTransportDelegate
151+
{
152+
public:
153+
void OnMessageDropped() override { mMessageDropped = true; }
154+
bool mMessageDropped = false;
155+
};
156+
157+
void TestPASESession::SecurePairingHandshake(SessionManager & sessionManager, PASESession & pairingCommissioner,
158+
TestSecurePairingDelegate & delegateCommissioner,
159+
TestSecurePairingDelegate & delegateAccessory, const Spake2pVerifier & verifier,
160+
uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t SetUpPINCode)
161+
{
162+
163+
PASESession pairingAccessory;
164+
165+
PASETestLoopbackTransportDelegate delegate;
166+
auto & loopback = GetLoopback();
167+
loopback.SetLoopbackTransportDelegate(&delegate);
168+
loopback.mSentMessageCount = 0;
169+
170+
ExchangeContext * contextCommissioner = NewUnauthenticatedExchangeToBob(&pairingCommissioner);
171+
172+
EXPECT_EQ(GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::PBKDFParamRequest,
173+
&pairingAccessory),
174+
CHIP_NO_ERROR);
175+
176+
pairingAccessory.WaitForPairing(sessionManager, verifier, pbkdf2IterCount, salt,
177+
Optional<ReliableMessageProtocolConfig>::Missing(), &delegateAccessory);
178+
DrainAndServiceIO();
179+
180+
pairingCommissioner.Pair(sessionManager, SetUpPINCode, Optional<ReliableMessageProtocolConfig>::Missing(), contextCommissioner,
181+
&delegateCommissioner);
182+
183+
DrainAndServiceIO();
184+
}
185+
186+
//----------------------------------------**********Fuzz Tests*********------------------------------------------------
187+
188+
// This Fuzz Test should always result in Successful PASE Pairing, since all fuzzed inputs are within the valid bounds
189+
void PASESession_Bounded(const uint32_t fuzzedSetupPasscode, const vector<uint8_t> & fuzzedSalt, uint32_t fuzzedPBKDF2Iter)
190+
{
191+
192+
Spake2pVerifier fuzzedSpake2pVerifier;
193+
ByteSpan fuzzedSaltSpan{ fuzzedSalt.data(), fuzzedSalt.size() };
194+
195+
// Generating the Spake2+ verifier from the fuzzed inputs
196+
EXPECT_EQ(fuzzedSpake2pVerifier.Generate(fuzzedPBKDF2Iter, fuzzedSaltSpan, fuzzedSetupPasscode), CHIP_NO_ERROR);
197+
198+
// TODO: #35369 Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved
199+
TestPASESession PASELoopBack;
200+
TemporarySessionManager sessionManager(PASELoopBack);
201+
202+
PASESession pairingCommissioner;
203+
204+
TestSecurePairingDelegate delegateCommissioner;
205+
TestSecurePairingDelegate delegateCommissionee;
206+
207+
PASELoopBack.SecurePairingHandshake(sessionManager, pairingCommissioner, delegateCommissioner, delegateCommissionee,
208+
fuzzedSpake2pVerifier, fuzzedPBKDF2Iter, fuzzedSaltSpan, fuzzedSetupPasscode);
209+
210+
// Given that the inputs to this Fuzz Test are within the expected boundaries, the Pairing should Always be successful.
211+
EXPECT_EQ(delegateCommissionee.mNumPairingComplete, 1u);
212+
EXPECT_EQ(delegateCommissioner.mNumPairingComplete, 1u);
213+
214+
EXPECT_EQ(delegateCommissionee.mNumPairingErrors, 0u);
215+
EXPECT_EQ(delegateCommissioner.mNumPairingErrors, 0u);
216+
}
217+
218+
FUZZ_TEST(FuzzPASE_PW, PASESession_Bounded)
219+
.WithDomains(
220+
InRange(00000000, 99999998),
221+
Arbitrary<vector<uint8_t>>().WithMinSize(kSpake2p_Min_PBKDF_Salt_Length).WithMaxSize(kSpake2p_Max_PBKDF_Salt_Length),
222+
InRange(kSpake2p_Min_PBKDF_Iterations, kSpake2p_Max_PBKDF_Iterations));
223+
224+
/* -------------------------------------------------------------------------------------------*/
225+
// This Fuzz Test is the equivalent of the previous one, but with the fuzzed inputs not being within the valid bounds.
226+
void PASESession_Unbounded(const uint32_t fuzzedSetupPasscode, const vector<uint8_t> & fuzzedSalt, uint32_t fuzzedPBKDF2Iter)
227+
{
228+
229+
Spake2pVerifier fuzzedSpake2pVerifier;
230+
ByteSpan fuzzedSaltSpan{ fuzzedSalt.data(), fuzzedSalt.size() };
231+
232+
// Generating the Spake2+ verifier from fuzzed inputs
233+
fuzzedSpake2pVerifier.Generate(fuzzedPBKDF2Iter, fuzzedSaltSpan, fuzzedSetupPasscode);
234+
235+
TestPASESession PASELoopBack;
236+
TemporarySessionManager sessionManager(PASELoopBack);
237+
238+
PASESession pairingCommissioner;
239+
240+
TestSecurePairingDelegate delegateCommissioner;
241+
TestSecurePairingDelegate delegateCommissionee;
242+
243+
PASELoopBack.SecurePairingHandshake(sessionManager, pairingCommissioner, delegateCommissioner, delegateCommissionee,
244+
fuzzedSpake2pVerifier, fuzzedPBKDF2Iter, fuzzedSaltSpan, fuzzedSetupPasscode);
245+
}
246+
247+
FUZZ_TEST(FuzzPASE_PW, PASESession_Unbounded)
248+
.WithDomains(Arbitrary<uint32_t>(), Arbitrary<vector<uint8_t>>(), Arbitrary<uint32_t>());
249+
250+
/* -------------------------------------------------------------------------------------------*/
251+
// In This FuzzTest, the Spake2pVerifier is fuzzed.
252+
void FuzzSpake2pVerifier(const vector<uint8_t> & aW0, const vector<uint8_t> & aL, const vector<uint8_t> & aSalt,
253+
const uint32_t fuzzedPBKDF2Iter, const uint32_t fuzzedSetupPasscode)
254+
{
255+
Spake2pVerifier fuzzedSpake2pVerifier;
256+
257+
copy_n(aW0.data(), aW0.size(), fuzzedSpake2pVerifier.mW0);
258+
copy_n(aL.data(), aL.size(), fuzzedSpake2pVerifier.mL);
259+
260+
ByteSpan fuzzedSaltSpan(aSalt.data(), aSalt.size());
261+
262+
TestPASESession PASELoopBack;
263+
TemporarySessionManager sessionManager(PASELoopBack);
264+
265+
PASESession pairingCommissioner;
266+
267+
TestSecurePairingDelegate delegateCommissioner;
268+
TestSecurePairingDelegate delegateCommissionee;
269+
270+
PASELoopBack.SecurePairingHandshake(sessionManager, pairingCommissioner, delegateCommissioner, delegateCommissionee,
271+
fuzzedSpake2pVerifier, fuzzedPBKDF2Iter, fuzzedSaltSpan, fuzzedSetupPasscode);
272+
}
273+
FUZZ_TEST(FuzzPASE_PW, FuzzSpake2pVerifier)
274+
.WithDomains(Arbitrary<std::vector<uint8_t>>().WithMaxSize(kP256_FE_Length),
275+
Arbitrary<std::vector<uint8_t>>().WithMaxSize(kP256_Point_Length), Arbitrary<vector<uint8_t>>(),
276+
Arbitrary<uint32_t>(), Arbitrary<uint32_t>());
277+
278+
/* -------------------------------------------------------------------------------------------*/
279+
// In This FuzzTest, Fuzzed Serialized Verifier is deserialized and Serialized Again, comparing the original with RoundTrip result.
280+
void Spake2pVerifier_Serialize_RoundTrip(const vector<uint8_t> & FuzzedSerializedVerifier)
281+
{
282+
283+
Spake2pVerifierSerialized FuzzedSerializedVerifierArray;
284+
285+
copy_n(FuzzedSerializedVerifier.data(), FuzzedSerializedVerifier.size(), FuzzedSerializedVerifierArray);
286+
287+
// Deserialize the fuzzed SPAKE2+ Verifier
288+
Spake2pVerifier verifier;
289+
EXPECT_EQ(verifier.Deserialize(ByteSpan(FuzzedSerializedVerifierArray)), CHIP_NO_ERROR);
290+
291+
// Serialize the fuzzed SPAKE2+ Verifier again
292+
Spake2pVerifierSerialized reserializedVerifier;
293+
MutableByteSpan reserializedVerifierSpan(reserializedVerifier);
294+
EXPECT_EQ(verifier.Serialize(reserializedVerifierSpan), CHIP_NO_ERROR);
295+
EXPECT_EQ(reserializedVerifierSpan.size(), kSpake2p_VerifierSerialized_Length);
296+
297+
// The original fuzzed SPAKE2+ verifier should be the same as the deserialized and re-serialized verifier (RoundTrip).
298+
EXPECT_EQ(memcmp(reserializedVerifier, FuzzedSerializedVerifierArray, kSpake2p_VerifierSerialized_Length), 0);
299+
}
300+
301+
FUZZ_TEST(FuzzPASE_PW, Spake2pVerifier_Serialize_RoundTrip)
302+
.WithDomains(Arbitrary<vector<uint8_t>>().WithSize(kSpake2p_VerifierSerialized_Length));
303+
304+
} // namespace

0 commit comments

Comments
 (0)