Sessions

tags: CHIP Matter Firmware Engineer

2022/06/05
CHIP git hash code 67b4746ad8

How message goes

Following the calling stack of sending/receiving message word by word shows a very basic idea of how the message being handled.

However, we will have a better understanding of this project if we take a look from structure point of view.

So, I would like to put down some note about these two classes:
Seesion and ExchangeContext(next post).

Sessions in PASE

The very first step to connect to the device is to use this command:

connect -ble 3840 20202021 1234

After BLE connection, both controller and device have a PASE session object ready for the incoming PASE handshaking. On the controller side, the PASESession is in CommissioneeDeviceProxy. This class is just like what it suggests, namely, controller uses it as a device proxy representing the devcie it is going to commit.

On the device, we can find the PASESession in CommissioningWindowManager.

Here we have:







G


cluster_controller

Controller


cluster_device

Device



c_PASESession

PASESession



d_PASESession

PASESession



Nonetheless, if we follow the calling stack of receiving message, we will notice that PASESession is not the session the CHIP stack uses to talk to each other.
In the SessionManager::MessageDispatch, we can find this:

Optional<Transport::UnauthenticatedSessionHandle> optionalSession = mUnauthenticatedSessions.FindOrAllocateEntry(peerAddress, gDefaultMRPConfig); /* * something else */ mCB->OnMessageReceived(packetHeader, payloadHeader, SessionHandle(session), peerAddress, isDuplicate, std::move(msg));

The peerAddress is bypassed by

/* in BLEBase::OnEndPointMessageReceived */ HandleMessageReceived(Transport::PeerAddress(Transport::Type::kBle), std::move(buffer));

As you can see, the session being bypassed to the upper layer is a session object called UnauthenticatedSessionHandle.

So, where is PASESesion?

It turns out that PASESession is more like a state machine which handle the entire PASE handshaking.

And after the PASE is finished, the CHIP stack will create a security session by the PASESession and free the PASEsession for next possible PASE request. Also, the CHIP stack could find this secursity session by peer Node ID or peer session ID later on.

For example, in CHIPDeviceController.cpp we have this:

CHIP_ERROR err = mSystemState->SessionMgr()->NewPairing(mDeviceBeingCommissioned->GetSecureSessionHolder(), Optional<Transport::PeerAddress>::Value(pairing->GetPeerAddress()),pairing->GetPeerNodeId(), pairing, CryptoContext::SessionRole::kInitiator, mFabricIndex);

Although it doesn't show the session ID, the "NewPairing" will actually read the peer session ID and the local session from the pairing object.

After that, both controller and device will reset some objects so that they could accept next PASE request.

But, what exactly are session ID and Node ID?

Session ID

Session ID is the ID generated by the CHIP stack, it should come with a session object(unauthenticated or authenticated) and should be unique on controller/device.

On controller side, we can find it in EstablishPASEConnection at CHIPDeviceController.cpp (keyID = session ID):

session = mSystemState->SessionMgr()->CreateUnauthenticatedSession(params.GetPeerAddress(), device->GetMRPConfig()); VerifyOrExit(session.HasValue(), err = CHIP_ERROR_NO_MEMORY); exchangeCtxt = mSystemState->ExchangeMgr()->NewContext(session.Value(), &device->GetPairing()); VerifyOrExit(exchangeCtxt != nullptr, err = CHIP_ERROR_INTERNAL); err = mIDAllocator.Allocate(keyID); SuccessOrExit(err); // TODO - Remove use of SetActive/IsActive from CommissioneeDeviceProxy device->SetActive(true); /* * If you check the API of Pair, you will see the third parameter is called sessionID */ err = device->GetPairing().Pair(params.GetPeerAddress(), params.GetSetupPINCode(), keyID, Optional<ReliableMessageProtocolConfig>::Value(mMRPConfig), exchangeCtxt, this);

For device, we can also find session ID in OpenCommissioningWindow at CommissioningWindowManager.cpp (again, keyID = sessionID)

ReturnErrorOnFailure(mIDAllocator->Allocate(keyID)); mPairingSession.Clear(); ReturnErrorOnFailure(mPairingSession.MessageDispatch().Init(&mServer->GetSecureSessionManager())); ... if (mUseECM) { ReturnErrorOnFailure(SetTemporaryDiscriminator(mECMDiscriminator)); ReturnErrorOnFailure( mPairingSession.WaitForPairing(mECMPASEVerifier, mECMIterations, ByteSpan(mECMSalt, mECMSaltLength), mECMPasscodeID, keyID, Optional<ReliableMessageProtocolConfig>::Value(gDefaultMRPConfig), this)); // reset all advertising, indicating we are in commissioningMode app::DnssdServer::Instance().StartServer(Dnssd::CommissioningMode::kEnabledEnhanced); } else { uint32_t pinCode; ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSetupPinCode(pinCode)); ReturnErrorOnFailure(mPairingSession.WaitForPairing( pinCode, kSpake2p_Iteration_Count, ByteSpan(reinterpret_cast<const uint8_t *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)), keyID, Optional<ReliableMessageProtocolConfig>::Value(gDefaultMRPConfig), this)); // reset all advertising, indicating we are in commissioningMode app::DnssdServer::Instance().StartServer(Dnssd::CommissioningMode::kEnabledBasic); }

In the above code, the session ID is stored in the PASESession at first, and the CHIP stack will copy this session ID from PASESession to the secruity session just like what I memtion above.

Node ID

For this command:

connect -ble 3840 20202021 1234

You will see this statement in the official document.

You can skip the last parameter, the Node ID, in the command. If you skip it, the controller will assign it randomly. In that case, note down the Node ID, because it is required later in the configuration process.

So, from this statement, we know

  1. The "1234" is generated by controller side, the device has zero knowledge about this Node ID at this moment.
  2. Also, this ID is important to the controller, and you need to write it down.

Thus, Node ID is just the ID bypassed from the top most layer - controller. And Node ID is used by controller side to recognize different device that the controller connects to. However, the "1234" is the ID for device, does the controller have an ID? The answer is yes, and the ID is generated randomly by the CHIP stack.

One way to check controller ID is by the log. After CASE handshaking, the log on device side would show this:
New secure session created for device 0x000000000001B669, key 7!!

And in NewPair at SessionManager.cpp, we have this:

ChipLogDetail(Inet, "New secure session created for device 0x" ChipLogFormatX64 ", key %d!!", ChipLogValueX64(peerNodeId),peerSessionId);

So I guess the 1B669 is the ID of the controller.

But, on the device side, it didn't know either the controller ID or the ID the controller would assign to it(for now). So at the moment the device finished the PASE, it would just use 0 as the Node ID. We have this log on device side after the device finished the PASE handshaking.

New secure session created for device 0x0000000000000000, key 1!!

Sessions in CASE

Sessions in CASE are quite similar to sessions in PASE. That is, the CASESession object itself is a state machine to controller the process of CASE, and there is another session, which is UnauthenticatedSession again, would be used to store information like address, port, node id, and etc. After the CASE is done, both controller and device would use hte CASESession to create a security session and save the security session somewhere else.

Here I simply show where the CASE session is. On the controller side, we can find the CASE session and the unauthenticated session in CASEClient::EstablishSession at CASEClient.cpp.

On the device side, almost like PASE, the device would create an unauthenticated session in SessionManager::SecureUnicastMessageDispatch.
Also, Server class has a member called "CASEServer mCASEServer" which registers for Sigma1 message, and mCASEServer is where device keeps its CASE session.

When the CASE is done, the controller would have a OperationalDeviceProxy object stored in CASESessionManager, and this proxy would has the security session.
On the device side, it will just store the security session in the SessionManager because it doesn't have to recognize the controller connected to it.

How many sessions

So, on both controller and device side, there is always one PASEsession and one CASEsession ready for the next handskaing request. In the meanwhile, they would also keep the established secursity session.

Here we could possibly have:







G


cluster_controller_2

Controller2


cluster_CASESessionMgr2

CASESessionManager


cluster_controller_1

Controller1


cluster_CASESessionMgr

CASESessionManager


cluster_device

Device


cluster_SessionMgr

SessionManager



c1_PASESession

PASESession



c1_CASESession

CASESession



d1_1

device1_session



c_1

established_session1



d1_1->c_1






d1_2

device2_session



other_dev1

other device session



d1_2->other_dev1






c2_PASESession

PASESession



c2_CASESession

CASESession



d2_1

device1_session



c_2

established_session2



d2_1->c_2






d2_2

device2_session



other_dev2

other device session



d2_2->other_dev2






d_PASESession

PASESession



d_CASESession

CASESession