diff --git a/tests/test_networkengine.cpp b/tests/test_networkengine.cpp index 8a0ee3d4..b5e80e14 100644 --- a/tests/test_networkengine.cpp +++ b/tests/test_networkengine.cpp @@ -1264,7 +1264,7 @@ NetworkEngineTester::testTransportPlaintextDoesNotMoveKeyedNode() // Answered, but only readable by the owner of the key CPPUNIT_ASSERT_EQUAL((size_t) 1, a.socket->sends.size()); CPPUNIT_ASSERT(a.socket->sends[0].dest == attacker); - CPPUNIT_ASSERT(isEnvelope(a.socket->sends[0], transport::EnvelopeType::Session)); + CPPUNIT_ASSERT(isEnvelope(a.socket->sends[0], transport::EnvelopeType::Initial)); } void @@ -1969,6 +1969,198 @@ NetworkEngineTester::testTransportEncryptedFragmentsRequireSenderKey() CPPUNIT_ASSERT(r.received()); } +void +NetworkEngineTester::testTransportPlaintextCannotPoisonSessionCache() +{ + TransportTestNode receiver("127.0.0.2", {}, 1); + TransportTestNode peer("127.0.0.3", {}, 2); + TransportTestNode victim("127.0.0.4", {}, 3); + establishSession(receiver, peer); + establishSession(receiver, victim); + const auto cached = receiver.engine->epoch_.sessions.size(); + const auto ephemeral = receiver.node(peer)->getTransport()->ephemeral; + CPPUNIT_ASSERT(receiver.engine->session(*receiver.node(peer)->getTransport(), victim.addr)); + CPPUNIT_ASSERT(receiver.engine->epoch_.find(victim.addr)->peer == victim.key.pub); + auto ping = makePingPacketBlob(peer.id, 77); + receiver.engine->processMessage(ping.data(), ping.size(), victim.addr); + receiver.takeSent(); + CPPUNIT_ASSERT(receiver.engine->epoch_.find(victim.addr)->peer == victim.key.pub); + + for (unsigned index = 0; index < 64; ++index) { + auto from = makeIPv4("203.0.113.7", 10000 + index); + receiver.engine->processMessage(ping.data(), ping.size(), from); + } + CPPUNIT_ASSERT_EQUAL(cached, receiver.engine->epoch_.sessions.size()); + CPPUNIT_ASSERT(receiver.node(peer)->getTransport()->ephemeral == ephemeral); + receiver.takeSent(); + + bool done = false; + victim.ping(victim.node(receiver), done); + victim.deliverTo(receiver); + CPPUNIT_ASSERT(isEnvelope(receiver.socket->sends.at(0), transport::EnvelopeType::Data)); + receiver.deliverTo(victim); + CPPUNIT_ASSERT(done); +} + +void +NetworkEngineTester::testTransportPlaintextCannotUpdateKeyedNode() +{ + TransportTestNode receiver("127.0.0.2", {}, 1); + TransportTestNode peer("127.0.0.3", {}, 2); + establishSession(receiver, peer); + size_t expired = 0; + auto socketId = receiver.node(peer)->openSocket([&](const Sp&, net::RequestAnswer&& answer) { + expired += answer.expired_values.size(); + }); + for (bool legacy : {false, true}) { + msgpack::sbuffer buffer; + msgpack::packer pk(&buffer); + pk.pack_map(legacy ? 3 : 4); + pk.pack(legacy ? KEY_U : KEY_A); + pk.pack_map(legacy ? 2 : 3); + pk.pack(KEY_REQ_ID); + pk.pack(peer.id); + if (not legacy) { + pk.pack(KEY_REQ_SID); + pk.pack(socketId); + } + pk.pack(KEY_REQ_EXPIRED); + pk.pack(std::vector {42}); + if (not legacy) { + pk.pack(KEY_Q); + pk.pack(QUERY_UPDATE); + } + pk.pack(KEY_TID); + pk.pack(legacy ? socketId : 77); + pk.pack(KEY_Y); + pk.pack(legacy ? KEY_R : KEY_Q); + for (const auto& from : {peer.addr, makeIPv4("127.0.0.66", 4222)}) { + receiver.engine->processMessage((const uint8_t*) buffer.data(), buffer.size(), from); + CPPUNIT_ASSERT_EQUAL((size_t) 0, expired); + CPPUNIT_ASSERT(receiver.socket->sends.empty()); + } + } + peer.engine->tellListenerExpired(peer.node(receiver), socketId, {}, {42}, 1); + peer.deliverTo(receiver); + CPPUNIT_ASSERT_EQUAL((size_t) 1, expired); +} + +void +NetworkEngineTester::testTransportPlaintextCannotCompleteEncryptedRequest() +{ + TransportTestNode receiver("127.0.0.2", {}, 1); + TransportTestNode peer("127.0.0.3", {}, 2); + establishSession(receiver, peer); + auto node = receiver.node(peer); + const auto ephemeral = node->getTransport()->ephemeral; + bool done = false; + auto request = receiver.ping(node, done); + auto reply = makeReplyHeaderPacketBlob(peer.id, request->getTid(), {}); + receiver.engine->processMessage(reply.data(), reply.size(), peer.addr); + CPPUNIT_ASSERT(not done); + CPPUNIT_ASSERT(request->pending()); + + auto advertisement = makePingPacketBlob(peer.id, 78, peer.key.pub); + receiver.engine->processMessage(advertisement.data(), advertisement.size(), peer.addr); + CPPUNIT_ASSERT(node->getTransport()->ephemeral == ephemeral); + CPPUNIT_ASSERT(receiver.engine->epoch_.find(peer.addr)->confirmed); + receiver.deliverTo(peer); + peer.deliverTo(receiver); + CPPUNIT_ASSERT(done); +} + +void +NetworkEngineTester::testTransportInitialReplayKeepsPeerState() +{ + TransportTestNode receiver("127.0.0.2", {}, 1); + TransportTestNode peer("127.0.0.3", {}, 2); + bool done = false; + receiver.ping(peer.addr, done); + receiver.deliverTo(peer); + auto initial = peer.socket->sends.at(0); + CPPUNIT_ASSERT(isEnvelope(initial, transport::EnvelopeType::Initial)); + peer.deliverTo(receiver); + establishSession(receiver, peer); + auto node = receiver.node(peer); + const auto ephemeral = node->getTransport()->ephemeral; + const auto newNodes = receiver.newNodes; + auto oldEphemeral = transport::KeyPair::generate(); + auto ping = makePingPacketBlob(peer.id, 79); + auto initialPing = transport::seal(*transport::initialSendKey(peer.key, oldEphemeral, receiver.key.pub, 0), + transport::EnvelopeType::Initial, + peer.key.pub, + oldEphemeral.pub, + ping.data(), + ping.size()); + for (const auto& from : {peer.addr, makeIPv4("127.0.0.66", 4222)}) { + for (const auto& packet : {initial.data, initialPing}) { + receiver.engine->processMessage(packet.data(), packet.size(), from); + CPPUNIT_ASSERT(node->getAddr() == peer.addr); + CPPUNIT_ASSERT(node->getTransport()->ephemeral == ephemeral); + CPPUNIT_ASSERT_EQUAL(newNodes, receiver.newNodes); + CPPUNIT_ASSERT(receiver.engine->epoch_.find(peer.addr)->confirmed); + } + } + receiver.takeSent(); + done = false; + receiver.ping(node, done); + CPPUNIT_ASSERT(isEnvelope(receiver.socket->sends.at(0), transport::EnvelopeType::Data)); + receiver.deliverTo(peer); + peer.deliverTo(receiver); + CPPUNIT_ASSERT(done); +} + +void +NetworkEngineTester::testTransportInitialAddressChangeNeedsSession() +{ + auto authority = transport::Authority::generate(); + for (bool isPrivate : {false, true}) { + auto config = isPrivate ? privateTransport(authority) : TransportConfig {}; + if (not config.key) + config.key = transport::KeyPair::generate(); + TransportTestNode peer("127.0.0.3", config, 2); + TransportTestNode receiver("127.0.0.2", isPrivate ? privateTransport(authority) : TransportConfig {}, 1); + establishSession(receiver, peer); + auto node = receiver.node(peer); + TransportTestNode moved("127.0.0.4", config, 3); + auto remote = moved.node(receiver); + remote->setTransport(std::make_shared(receiver.key.pub)); + bool done = false; + moved.ping(remote, done); + moved.deliverTo(receiver); + CPPUNIT_ASSERT(node->getAddr() == peer.addr); + receiver.deliverTo(moved); + CPPUNIT_ASSERT(done); + + done = false; + moved.ping(remote, done); + CPPUNIT_ASSERT(isEnvelope(moved.socket->sends.at(0), transport::EnvelopeType::Session, isPrivate)); + moved.deliverTo(receiver); + CPPUNIT_ASSERT(node->getAddr() == moved.addr); + receiver.deliverTo(moved); + CPPUNIT_ASSERT(done); + } +} + +void +NetworkEngineTester::testTransportEncryptionFailureDoesNotSendPlaintext() +{ + TransportTestNode receiver("127.0.0.2", {}, 1); + auto from = makeIPv4("127.0.0.3", 4222); + transport::Key lowOrder {}; + lowOrder[0] = 1; + auto ping = makePingPacketBlob(transport::nodeId(lowOrder), 80, lowOrder); + receiver.engine->processMessage(ping.data(), ping.size(), from); + CPPUNIT_ASSERT(receiver.socket->sends.empty()); + + auto peer = std::make_shared(transport::KeyPair::generate().pub); + peer->ephemeral = lowOrder; + auto node = receiver.engine->insertNode(transport::nodeId(peer->key), from); + node->setTransport(peer); + CPPUNIT_ASSERT_EQUAL(EPERM, receiver.engine->send(*node, from, "payload", 7)); + CPPUNIT_ASSERT(receiver.socket->sends.empty()); +} + } // namespace test #endif \ No newline at end of file diff --git a/tests/test_networkengine.h b/tests/test_networkengine.h index 1105fd0a..6deffd94 100644 --- a/tests/test_networkengine.h +++ b/tests/test_networkengine.h @@ -50,6 +50,12 @@ class NetworkEngineTester : public CppUnit::TestFixture CPPUNIT_TEST(testTransportEncryptedFragmentsIgnorePlaintextParts); CPPUNIT_TEST(testTransportEncryptedFragmentsIgnorePlaintextHeader); CPPUNIT_TEST(testTransportEncryptedFragmentsRequireSenderKey); + CPPUNIT_TEST(testTransportPlaintextCannotPoisonSessionCache); + CPPUNIT_TEST(testTransportPlaintextCannotUpdateKeyedNode); + CPPUNIT_TEST(testTransportPlaintextCannotCompleteEncryptedRequest); + CPPUNIT_TEST(testTransportInitialReplayKeepsPeerState); + CPPUNIT_TEST(testTransportInitialAddressChangeNeedsSession); + CPPUNIT_TEST(testTransportEncryptionFailureDoesNotSendPlaintext); #endif CPPUNIT_TEST_SUITE_END(); @@ -97,6 +103,12 @@ public: void testTransportEncryptedFragmentsIgnorePlaintextParts(); void testTransportEncryptedFragmentsIgnorePlaintextHeader(); void testTransportEncryptedFragmentsRequireSenderKey(); + void testTransportPlaintextCannotPoisonSessionCache(); + void testTransportPlaintextCannotUpdateKeyedNode(); + void testTransportPlaintextCannotCompleteEncryptedRequest(); + void testTransportInitialReplayKeepsPeerState(); + void testTransportInitialAddressChangeNeedsSession(); + void testTransportEncryptionFailureDoesNotSendPlaintext(); #endif };