Skip to content

Commit

Permalink
[tcat] implementation of TCAT general class commands.
Browse files Browse the repository at this point in the history
Commit introduces implementation of missing general class commands:
- PresentPskdHash
- PresentPskcHash
- PresentInstallCodeHash
- RequestRandomNumChallenge
- RequestPskdHash

Also include minor fixes in Tcat python client and refactoring of expect
tests for tcat.
  • Loading branch information
canisLupus1313 committed Sep 13, 2024
1 parent 4459c54 commit d1b7fc3
Show file tree
Hide file tree
Showing 16 changed files with 480 additions and 45 deletions.
6 changes: 4 additions & 2 deletions src/cli/cli_tcat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ namespace Cli {
otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX];
otTcatGeneralDeviceId sGeneralDeviceId;

const char kPskdVendor[] = "JJJJJJ";
const char kUrl[] = "dummy_url";
const char kPskdVendor[] = "JJJJJJ";
const char kInstallVendor[] = "InstallCode";
const char kUrl[] = "dummy_url";

static bool IsDeviceIdSet(void)
{
Expand Down Expand Up @@ -250,6 +251,7 @@ template <> otError Tcat::Process<Cmd("start")>(Arg aArgs[])
ClearAllBytes(mVendorInfo);
mVendorInfo.mPskdString = kPskdVendor;
mVendorInfo.mProvisioningUrl = kUrl;
mVendorInfo.mInstallCode = kInstallVendor;

if (IsDeviceIdSet())
{
Expand Down
1 change: 0 additions & 1 deletion src/core/meshcop/secure_transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,6 @@ Error SecureTransport::Setup(bool aClient)
int SecureTransport::SetApplicationSecureKeys(void)
{
int rval = 0;

switch (mCipherSuites[0])
{
case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
Expand Down
9 changes: 9 additions & 0 deletions src/core/meshcop/secure_transport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,15 @@ class SecureTransport : public InstanceLocator
*/
void HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);

/**
* Extracts public key from it's own certificate.
*
* @return public key from own certificate in form of entire ASN.1 field.
*
*/

mbedtls_asn1_buf *GetOwnPublicKey(void) { return &mOwnCert.pk_raw; }

private:
enum State : uint8_t
{
Expand Down
153 changes: 151 additions & 2 deletions src/core/meshcop/tcat_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
*/

#include "tcat_agent.hpp"
#include <cstddef>
#include <openthread/tcat.h>
#include <openthread/platform/toolchain.h>
#include "common/error.hpp"
#include "meshcop/network_name.hpp"
#include "thread/key_manager.hpp"

#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE

Expand All @@ -42,6 +46,7 @@
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/locator_getters.hpp"
#include "common/random.hpp"
#include "common/string.hpp"
#include "instance/instance.hpp"
#include "radio/radio.hpp"
Expand Down Expand Up @@ -71,6 +76,7 @@ TcatAgent::TcatAgent(Instance &aInstance)
, mCommissionerHasNetworkName(false)
, mCommissionerHasDomainName(false)
, mCommissionerHasExtendedPanId(false)
, mRandomChallenge(0)
{
mJoinerPskd.Clear();
mCurrentServiceName[0] = 0;
Expand All @@ -84,6 +90,7 @@ Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallb
VerifyOrExit(mVendorInfo != nullptr, error = kErrorFailed);
mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext);
mJoinCallback.Set(aHandler, aContext);
mRandomChallenge = 0;

mCurrentApplicationProtocol = kApplicationProtocolNone;
mState = kStateEnabled;
Expand All @@ -99,6 +106,7 @@ void TcatAgent::Stop(void)
mState = kStateDisabled;
mAppDataReceiveCallback.Clear();
mJoinCallback.Clear();
mRandomChallenge = 0;
LogInfo("TCAT agent stopped");
}

Expand Down Expand Up @@ -180,6 +188,7 @@ void TcatAgent::Disconnected(void)
{
mState = kStateEnabled;
}
mRandomChallenge = 0;

LogInfo("TCAT agent disconnected");
}
Expand Down Expand Up @@ -436,7 +445,21 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
case kTlvGetProvisioningURL:
error = HandleGetProvisioningUrl(aOutgoingMessage, response);
break;

case kTlvPresentPskdHash:
error = HandlePresentPskdHash(aIncomingMessage, offset, length);
break;
case kTlvPresentPskcHash:
error = HandlePresentPskcHash(aIncomingMessage, offset, length);
break;
case kTlvPresentInstallCodeHash:
error = HandlePresentInstallCodeHash(aIncomingMessage, offset, length);
break;
case kTlvRequestRandomNumChallenge:
error = HandleRequestRandomNumberChallenge(aOutgoingMessage, response);
break;
case kTlvRequestPskdHash:
error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response);
break;
default:
error = kErrorInvalidCommand;
}
Expand Down Expand Up @@ -471,6 +494,10 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
statusCode = kStatusUnsupported;
break;

case kErrorSecurity:
statusCode = kStatusHashError;
break;

default:
statusCode = kStatusGeneralError;
break;
Expand Down Expand Up @@ -629,13 +656,135 @@ Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &respo
length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength);
VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState);

error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length);
SuccessOrExit(error =
Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length));
response = true;

exit:
return error;
}

Error TcatAgent::HandlePresentPskdHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;

VerifyOrExit(mVendorInfo->mPskdString != nullptr, error = kErrorSecurity);

error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mPskdString,
StringLength(mVendorInfo->mPskdString, kMaxPSKdLength));

exit:
return error;
}

Error TcatAgent::HandlePresentPskcHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;
Dataset::Info datasetInfo;
Pskc pskc;

VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorSecurity);
VerifyOrExit(datasetInfo.IsPresent<Dataset::kPskc>(), error = kErrorSecurity);
pskc = datasetInfo.Get<Dataset::kPskc>();

error = VerifyHash(aIncomingMessage, aOffset, aLength, reinterpret_cast<const char *>(pskc.m8), Pskc::kSize);

exit:
return error;
}

Error TcatAgent::HandlePresentInstallCodeHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;

VerifyOrExit(mVendorInfo->mInstallCode != nullptr, error = kErrorSecurity);

error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mInstallCode,
StringLength(mVendorInfo->mInstallCode, 255));

exit:
return error;
}

Error TcatAgent::HandleRequestRandomNumberChallenge(Message &aOutgoingMessage, bool &response)
{
Error error = kErrorNone;

SuccessOrExit(error = Random::Crypto::Fill(mRandomChallenge));

SuccessOrExit(
error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, &mRandomChallenge, sizeof(mRandomChallenge)));
response = true;
exit:
return error;
}

Error TcatAgent::HandleRequestPskdHash(const Message &aIncomingMessage,
Message &aOutgoingMessage,
uint16_t aOffset,
uint16_t aLength,
bool &response)
{
Error error = kErrorNone;
uint64_t providedChallenge = 0;
Crypto::HmacSha256::Hash hash;

VerifyOrExit(StringLength(mVendorInfo->mPskdString, kMaxPSKdLength) != 0, error = kErrorFailed);
VerifyOrExit(aLength == sizeof(providedChallenge), error = kErrorParse);

SuccessOrExit(error = aIncomingMessage.Read(aOffset, &providedChallenge, aLength));

hash = CalculateHash(providedChallenge, mVendorInfo->mPskdString,
StringLength(mVendorInfo->mPskdString, kMaxPSKdLength));

SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, hash.GetBytes(),
Crypto::HmacSha256::Hash::kSize));
response = true;

exit:
return error;
}

Error TcatAgent::VerifyHash(const Message &aIncomingMessage,
uint16_t aOffset,
uint16_t aLength,
const char *aBuf,
size_t aBufLen)
{
Error error = kErrorNone;
Crypto::HmacSha256::Hash hash;
Crypto::HmacSha256::Hash recivedHash;

VerifyOrExit(aLength == Crypto::HmacSha256::Hash::kSize, error = kErrorSecurity);
VerifyOrExit(mRandomChallenge != 0, error = kErrorSecurity);

SuccessOrExit(error = aIncomingMessage.Read(aOffset, recivedHash.m8, aLength));

hash = CalculateHash(mRandomChallenge, aBuf, aBufLen);

VerifyOrExit(hash == recivedHash, error = kErrorSecurity);

exit:
return error;
}

Crypto::HmacSha256::Hash TcatAgent::CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen)
{
mbedtls_asn1_buf *rawKey = Get<Ble::BleSecure>().GetOwnPublicKey();
Crypto::Key cryptoKey;
Crypto::HmacSha256 hmac;
Crypto::HmacSha256::Hash hash;

cryptoKey.Set(rawKey->p, rawKey->len);

hmac.Start(cryptoKey);
hmac.Update(aChallenge);
hmac.Update(aBuf, aBufLen);
hmac.Finish(hash);

return hash;
}

Error TcatAgent::HandleStartThreadInterface(void)
{
Error error;
Expand Down
18 changes: 18 additions & 0 deletions src/core/meshcop/tcat_agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,24 @@ class TcatAgent : public InstanceLocator, private NonCopyable
Error HandleGetDeviceId(Message &aOutgoingMessage, bool &response);
Error HandleGetExtPanId(Message &aOutgoingMessage, bool &response);
Error HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response);
Error HandlePresentPskdHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
Error HandlePresentPskcHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
Error HandlePresentInstallCodeHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
Error HandleRequestRandomNumberChallenge(Message &aOutgoingMessage, bool &response);
Error HandleRequestPskdHash(const Message &aIncomingMessage,
Message &aOutgoingMessage,
uint16_t aOffset,
uint16_t aLength,
bool &response);
Error HandleStartThreadInterface(void);

Error VerifyHash(const Message &aIncomingMessage,
uint16_t aOffset,
uint16_t aLength,
const char *aBuf,
size_t aBufLen);
Crypto::HmacSha256::Hash CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen);

bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
CommandClassFlags aDeviceCommandClassFlags,
Dataset *aDataset) const;
Expand All @@ -374,6 +390,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable
static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT;
static constexpr uint16_t kPingPayloadMaxLength = 512;
static constexpr uint16_t kProvisioningUrlMaxLength = 64;
static constexpr uint16_t kMaxPSKdLength = OT_JOINER_MAX_PSKD_LENGTH;
static constexpr uint16_t kTcatMaxDeviceIdSize = OT_TCAT_MAX_DEVICEID_SIZE;

JoinerPskd mJoinerPskd;
Expand All @@ -391,6 +408,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable
bool mCommissionerHasNetworkName : 1;
bool mCommissionerHasDomainName : 1;
bool mCommissionerHasExtendedPanId : 1;
uint64_t mRandomChallenge;

friend class Ble::BleSecure;
};
Expand Down
9 changes: 9 additions & 0 deletions src/core/radio/ble_secure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,15 @@ class BleSecure : public InstanceLocator, private NonCopyable
return mTls.GetThreadAttributeFromOwnCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
}

/**
* Extracts public key from it's own certificate.
*
* @return public key from own certificate in form of entire ASN.1 field.
*
*/

mbedtls_asn1_buf *GetOwnPublicKey(void) { return mTls.GetOwnPublicKey(); }

/**
* Sets the authentication mode for the BLE secure connection. It disables or enables the verification
* of peer certificate.
Expand Down
31 changes: 31 additions & 0 deletions tests/scripts/expect/_common.exp
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,35 @@ proc fail {message} {
error $message
}

proc spawn_tcat_client_for_node {id} {
global tcat_ids
global spawn_id

switch_node $id

send "tcat start\n"
expect_line "Done"

spawn python "tools/tcat_ble_client/bbtc.py" --simulation $id --cert_path "tools/tcat_ble_client/auth"
expect_line "Done"

set tcat_ids($id) $spawn_id

return $spawn_id
}

proc switch_tcat_client_for_node {id} {
global tcat_ids
global spawn_id

send_user "\n# ${id}\n"
set spawn_id $tcat_ids($id)
}

proc dispose_tcat_client {id} {
switch_tcat_client_for_node $id
send "exit\n"
expect eof
}

set timeout 10
3 changes: 2 additions & 1 deletion tests/scripts/expect/cli-tcat-advertisement.exp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ source "tests/scripts/expect/_common.exp"

spawn_node 1 "cli"

switch_node 1
send "tcat advid ianapen f378\n"
expect_line "Done"

Expand Down Expand Up @@ -89,3 +88,5 @@ expect_line "Done"

send "tcat devid\n"
expect_line "Done"

dispose_all
Loading

0 comments on commit d1b7fc3

Please sign in to comment.