-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented CASEServer to fetch credentials, and wait for SigmaR1 #6791
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* | ||
* Copyright (c) 2021 Project CHIP Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <protocols/secure_channel/CASEServer.h> | ||
|
||
#include <core/CHIPError.h> | ||
#include <support/CodeUtils.h> | ||
#include <support/SafeInt.h> | ||
#include <transport/SecureSessionMgr.h> | ||
|
||
using namespace ::chip::Inet; | ||
using namespace ::chip::Transport; | ||
|
||
namespace chip { | ||
|
||
CHIP_ERROR CASEServer::ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr, | ||
SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins) | ||
{ | ||
VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
VerifyOrReturnError(sessionMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
VerifyOrReturnError(admins != nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
|
||
mSessionMgr = sessionMgr; | ||
mAdmins = admins; | ||
mExchangeManager = exchangeManager; | ||
|
||
ReturnErrorOnFailure(mPairingSession.MessageDispatch().Init(transportMgr)); | ||
|
||
ReturnErrorOnFailure( | ||
mExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1, this)); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
CHIP_ERROR CASEServer::InitCASEHandshake(Messaging::ExchangeContext * ec) | ||
{ | ||
ReturnErrorCodeIf(ec == nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
|
||
// Lookup the admin that corresponds to the CASE session setup request. | ||
// Each admin provisions their own credentials on the device. So it's essential to | ||
// use the correct operational certificates for CASE session setup. | ||
mAdminId = ec->GetSecureSession().GetAdminId(); | ||
ReturnErrorCodeIf(mAdminId == Transport::kUndefinedAdminId, CHIP_ERROR_INVALID_ARGUMENT); | ||
|
||
Transport::AdminPairingInfo * admin = mAdmins->FindAdminWithId(mAdminId); | ||
ReturnErrorCodeIf(admin == nullptr, CHIP_ERROR_INVALID_ARGUMENT); | ||
|
||
ReturnErrorOnFailure(admin->GetOperationalCertificateSet(mCertificates)); | ||
|
||
mCredentials.Release(); | ||
ReturnErrorOnFailure(mCredentials.Init(&mCertificates, mCertificates.GetCertCount())); | ||
|
||
// Setup CASE state machine using the credentials for the current admin. | ||
ReturnErrorOnFailure(mPairingSession.ListenForSessionEstablishment(&mCredentials, mNextKeyId++, this)); | ||
|
||
// Hand over the exchange context to the CASE session. | ||
ec->SetDelegate(&mPairingSession); | ||
|
||
return CHIP_NO_ERROR; | ||
} | ||
|
||
void CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, | ||
const PayloadHeader & payloadHeader, System::PacketBufferHandle payload) | ||
{ | ||
ReturnOnFailure(InitCASEHandshake(ec)); | ||
mPairingSession.OnMessageReceived(ec, packetHeader, payloadHeader, std::move(payload)); | ||
|
||
// TODO - Enable multiple concurrent CASE session establishment | ||
// This will prevent CASEServer to process another CASE session establishment request until the current | ||
// one completes (successfully or failed) | ||
mExchangeManager->UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1); | ||
} | ||
|
||
void CASEServer::Cleanup() | ||
{ | ||
// Let's re-register for CASE SigmaR1 message, so that the next CASE session setup request can be processed. | ||
mExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1, this); | ||
mAdminId = Transport::kUndefinedAdminId; | ||
mCredentials.Release(); | ||
} | ||
|
||
void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err) | ||
{ | ||
ChipLogProgress(AppServer, "CASE Session establishment failed: %s", ErrorStr(err)); | ||
Cleanup(); | ||
} | ||
|
||
void CASEServer::OnSessionEstablished() | ||
{ | ||
ChipLogProgress(AppServer, "CASE Session established. Setting up the secure channel."); | ||
CHIP_ERROR err = | ||
mSessionMgr->NewPairing(Optional<Transport::PeerAddress>::Value(mPairingSession.PeerConnection().GetPeerAddress()), | ||
mPairingSession.PeerConnection().GetPeerNodeId(), &mPairingSession, | ||
SecureSession::SessionRole::kResponder, mAdminId, nullptr); | ||
if (err != CHIP_NO_ERROR) | ||
{ | ||
ChipLogError(Ble, "Failed in setting up secure channel: err %s", ErrorStr(err)); | ||
OnSessionEstablishmentError(err); | ||
return; | ||
} | ||
|
||
ChipLogProgress(AppServer, "CASE secure channel is available now."); | ||
Cleanup(); | ||
} | ||
} // namespace chip |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* | ||
* Copyright (c) 2021 Project CHIP Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <messaging/ExchangeDelegate.h> | ||
#include <messaging/ExchangeMgr.h> | ||
#include <protocols/secure_channel/CASESession.h> | ||
|
||
namespace chip { | ||
|
||
class CASEServer : public SessionEstablishmentDelegate, public Messaging::ExchangeDelegateBase | ||
{ | ||
public: | ||
CASEServer() {} | ||
~CASEServer() | ||
{ | ||
if (mExchangeManager != nullptr) | ||
{ | ||
mExchangeManager->UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1); | ||
} | ||
|
||
mCredentials.Release(); | ||
} | ||
|
||
CHIP_ERROR ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr, | ||
SecureSessionMgr * sessionMgr, Transport::AdminPairingTable * admins); | ||
|
||
//////////// SessionEstablishmentDelegate Implementation /////////////// | ||
void OnSessionEstablishmentError(CHIP_ERROR error) override; | ||
void OnSessionEstablished() override; | ||
|
||
void Cleanup(); | ||
pan-apple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
uint16_t GetNextKeyId() const { return mNextKeyId; } | ||
void SetNextKeyId(uint16_t id) { mNextKeyId = id; } | ||
pan-apple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
//// ExchangeDelegate Implementation //// | ||
void OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, | ||
System::PacketBufferHandle payload) override; | ||
void OnResponseTimeout(Messaging::ExchangeContext * ec) override {} | ||
Messaging::ExchangeMessageDispatch * GetMessageDispatch(Messaging::ReliableMessageMgr * reliableMessageManager, | ||
SecureSessionMgr * sessionMgr) override | ||
{ | ||
return mPairingSession.GetMessageDispatch(reliableMessageManager, sessionMgr); | ||
} | ||
|
||
private: | ||
Messaging::ExchangeManager * mExchangeManager = nullptr; | ||
|
||
CASESession mPairingSession; | ||
uint16_t mNextKeyId = 0; | ||
SecureSessionMgr * mSessionMgr = nullptr; | ||
|
||
Transport::AdminId mAdminId = Transport::kUndefinedAdminId; | ||
|
||
Transport::AdminPairingTable * mAdmins = nullptr; | ||
ChipCertificateSet mCertificates; | ||
OperationalCredentialSet mCredentials; | ||
Comment on lines
+67
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fact that we end up having to make copies of this stuff is not great. Ideally we would just hold on to the right admin and use these things in-place from there. Followup for that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am expecting some refactor/cleanup once CASE state machine is hooked up on controller and device side. A lot of these overheads will iron out at that point. |
||
|
||
CHIP_ERROR InitCASEHandshake(Messaging::ExchangeContext * ec); | ||
}; | ||
|
||
} // namespace chip |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,8 @@ | |
#include <transport/AdminPairingTable.h> | ||
|
||
namespace chip { | ||
using namespace Credentials; | ||
using namespace Crypto; | ||
|
||
namespace Transport { | ||
|
||
|
@@ -49,7 +51,7 @@ CHIP_ERROR AdminPairingInfo::StoreIntoKVS(PersistentStorageDelegate * kvs) | |
} | ||
else | ||
{ | ||
Crypto::P256Keypair keypair; | ||
P256Keypair keypair; | ||
SuccessOrExit(err = keypair.Initialize()); | ||
SuccessOrExit(err = keypair.Serialize(info->mOperationalKey)); | ||
} | ||
|
@@ -115,7 +117,7 @@ CHIP_ERROR AdminPairingInfo::FetchFromKVS(PersistentStorageDelegate * kvs) | |
|
||
if (mOperationalKey == nullptr) | ||
{ | ||
mOperationalKey = chip::Platform::New<Crypto::P256Keypair>(); | ||
mOperationalKey = chip::Platform::New<P256Keypair>(); | ||
} | ||
VerifyOrExit(mOperationalKey != nullptr, err = CHIP_ERROR_NO_MEMORY); | ||
SuccessOrExit(err = mOperationalKey->Deserialize(info->mOperationalKey)); | ||
|
@@ -160,13 +162,13 @@ CHIP_ERROR AdminPairingInfo::GenerateKey(AdminId id, char * key, size_t len) | |
return CHIP_NO_ERROR; | ||
} | ||
|
||
CHIP_ERROR AdminPairingInfo::SetOperationalKey(const Crypto::P256Keypair & key) | ||
CHIP_ERROR AdminPairingInfo::SetOperationalKey(const P256Keypair & key) | ||
{ | ||
Crypto::P256SerializedKeypair serialized; | ||
P256SerializedKeypair serialized; | ||
ReturnErrorOnFailure(key.Serialize(serialized)); | ||
if (mOperationalKey == nullptr) | ||
{ | ||
mOperationalKey = chip::Platform::New<Crypto::P256Keypair>(); | ||
mOperationalKey = chip::Platform::New<P256Keypair>(); | ||
} | ||
VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY); | ||
return mOperationalKey->Deserialize(serialized); | ||
|
@@ -248,6 +250,20 @@ CHIP_ERROR AdminPairingInfo::SetOperationalCert(const ByteSpan & cert) | |
return CHIP_NO_ERROR; | ||
} | ||
|
||
CHIP_ERROR AdminPairingInfo::GetOperationalCertificateSet(ChipCertificateSet & certSet) | ||
{ | ||
constexpr uint8_t kMaxNumCertsInOpCreds = 3; | ||
ReturnErrorOnFailure(certSet.Init(kMaxNumCertsInOpCreds, kMaxChipCertSize * kMaxNumCertsInOpCreds)); | ||
|
||
ReturnErrorOnFailure( | ||
certSet.LoadCert(mRootCert, mRootCertLen, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So here, if we stored the certs as a ChipCertificateSet to start with, couldn't we just return a const ref to it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am expecting some refactor/cleanup once CASE state machine is hooked up on controller and device side. A lot of these overheads will iron out at that point. |
||
BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor).Set(CertDecodeFlags::kGenerateTBSHash))); | ||
// TODO - Add support of ICA certificates | ||
ReturnErrorOnFailure( | ||
certSet.LoadCert(mOperationalCert, mOpCertLen, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash))); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
AdminPairingInfo * AdminPairingTable::AssignAdminId(AdminId adminId) | ||
{ | ||
for (size_t i = 0; i < CHIP_CONFIG_MAX_DEVICE_ADMINS; i++) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indexing and lookup up by Admin ID breaks scoping. Per the spec, the admin must be located based upon the root PK of the root CA for the requesting agent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. We can do that as part of multi admin feature.
Would you prefer if I updated the above comment as part of this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A TODO seems appropriate. It doesn't have to happen now. I'm just pointing out that this code does not appear to be compliant with the
4.369 Validate Sigma1
algorithm.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the TODO here: https://github.com/project-chip/connectedhomeip/pull/6810/files#diff-c9e9c579ef5bbdbb97683c09136f253a842efdbb8d8febaf3e92ca12b47a0dddR56