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 12, 2024
1 parent 4459c54 commit a16bca9
Show file tree
Hide file tree
Showing 16 changed files with 476 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
4 changes: 3 additions & 1 deletion src/core/meshcop/secure_transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "secure_transport.hpp"

#include <mbedtls/debug.h>
#include "common/error.hpp"
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#include <mbedtls/pem.h>
#endif
Expand Down Expand Up @@ -425,7 +426,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 Expand Up @@ -480,6 +480,8 @@ int SecureTransport::SetApplicationSecureKeys(void)
return rval;
}

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

#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE

void SecureTransport::Close(void)
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);

private:
enum State : uint8_t
{
Expand Down
152 changes: 150 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,134 @@ 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;
Crypto::HmacSha256::Hash hash;
Crypto::HmacSha256::Hash recivedHash;

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

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

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

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

exit:
return error;
}

Error TcatAgent::HandlePresentPskcHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;
Crypto::HmacSha256 hmac;
Crypto::HmacSha256::Hash hash;
Crypto::HmacSha256::Hash recivedHash;
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>();

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

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

hash = CalculateHash(mRandomChallenge, reinterpret_cast<const char *>(pskc.m8), Pskc::kSize);

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

exit:
return error;
}

Error TcatAgent::HandlePresentInstallCodeHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;
Crypto::HmacSha256::Hash hash;
Crypto::HmacSha256::Hash recivedHash;

VerifyOrExit(mRandomChallenge != 0, error = kErrorFailed);
VerifyOrExit(aLength == Crypto::HmacSha256::Hash::kSize, error = kErrorSecurity);
VerifyOrExit(mVendorInfo->mInstallCode != nullptr, error = kErrorSecurity);

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

hash = CalculateHash(mRandomChallenge, mVendorInfo->mInstallCode, StringLength(mVendorInfo->mInstallCode, 255));

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

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 == 8, 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;
}

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
12 changes: 12 additions & 0 deletions src/core/meshcop/tcat_agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,17 @@ 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);
Crypto::HmacSha256::Hash CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen);

bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
CommandClassFlags aDeviceCommandClassFlags,
Expand All @@ -374,6 +384,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 +402,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 a16bca9

Please sign in to comment.