|
| 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