[tcat] implement extraction of active dataset and commissioner cert (#10991)

Commit adds implementation of:
	- 0x40 Tcat tlv extraction of active dataset,
	- 0x25 Tcat tlv extraction of commissioner certificate.

Includes also refactoring of `BleCommand` adds new method `process_response`.
This simplifies:
- `GetPskdHash`
- `GetRandomNumberChallenge`
This commit is contained in:
Przemysław Bida
2025-01-21 17:43:19 +01:00
committed by GitHub
parent 8c0363c79b
commit 4d6def38a5
16 changed files with 399 additions and 161 deletions
+20
View File
@@ -171,6 +171,26 @@ void otBleSecureSetPsk(otInstance *aInstance,
*/
otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength);
/**
* Returns the DER encoded peer x509 certificate.
*
* @note Requires the build-time feature `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to
* be enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aPeerCert A pointer to the DER encoded certificate
* buffer.
* @param[in,out] aCertLength On input, the size the max size of @p
* aPeerCert. On output, the length of the
* DER encoded peer certificate.
*
* @retval OT_ERROR_NONE Successfully get the peer certificate.
* @retval OT_ERROR_INVALID_ARGS @p aInstance or @p aCertLength is invalid.
* @retval OT_ERROR_INVALID_STATE Not connected yet.
* @retval OT_ERROR_NO_BUFS Can't allocate memory for certificate.
*/
otError otBleSecureGetPeerCertificateDer(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength);
/**
* Returns an attribute value identified by its OID from the subject
* of the peer x509 certificate. The peer OID is provided in binary format.
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (471)
#define OPENTHREAD_API_VERSION (472)
/**
* @addtogroup api-instance
+1
View File
@@ -72,6 +72,7 @@ enum
OT_SETTINGS_KEY_BR_ULA_PREFIX = 0x000f, ///< BR ULA prefix.
OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES = 0x0010, ///< BR local on-link prefixes.
OT_SETTINGS_KEY_BORDER_AGENT_ID = 0x0011, ///< Unique Border Agent/Router ID.
OT_SETTINGS_KEY_TCAT_COMMR_CERT = 0x0012, ///< TCAT Commissioner certificate
// Deprecated and reserved key values:
//
+10
View File
@@ -87,7 +87,9 @@ otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char
{
Error error;
VerifyOrExit(aPeerCert != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aCertLength != nullptr, error = kErrorInvalidArgs);
error = AsCoreType(aInstance).Get<Ble::BleSecure>().GetPeerCertificateBase64(aPeerCert, aCertLength, *aCertLength);
exit:
@@ -96,6 +98,14 @@ exit:
#endif
#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
otError otBleSecureGetPeerCertificateDer(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength)
{
AssertPointerIsNotNull(aPeerCert);
AssertPointerIsNotNull(aCertLength);
return AsCoreType(aInstance).Get<Ble::BleSecure>().GetPeerCertificateDer(aPeerCert, aCertLength, *aCertLength);
}
otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance,
const char *aOid,
size_t aOidLength,
+15 -2
View File
@@ -168,7 +168,8 @@ const char *SettingsBase::KeyToString(Key aKey)
"", // (14) Removed (previously NAT64 prefix)
"BrUlaPrefix", // (15) kKeyBrUlaPrefix
"BrOnLinkPrefixes", // (16) kKeyBrOnLinkPrefixes
"BorderAgentId" // (17) kKeyBorderAgentId
"BorderAgentId", // (17) kKeyBorderAgentId
"TcatCommrCert" // (18) kKeyTcatCommrCert
};
struct EnumCheck
@@ -192,9 +193,10 @@ const char *SettingsBase::KeyToString(Key aKey)
ValidateNextEnum(kKeyBrUlaPrefix);
ValidateNextEnum(kKeyBrOnLinkPrefixes);
ValidateNextEnum(kKeyBorderAgentId);
ValidateNextEnum(kKeyTcatCommrCert);
};
static_assert(kLastKey == kKeyBorderAgentId, "kLastKey is not valid");
static_assert(kLastKey == kKeyTcatCommrCert, "kLastKey is not valid");
OT_ASSERT(aKey <= kLastKey);
@@ -263,6 +265,17 @@ void Settings::DeleteOperationalDataset(MeshCoP::Dataset::Type aType)
OT_ASSERT(error != kErrorNotImplemented);
}
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
void Settings::SaveTcatCommissionerCertificate(uint8_t *aCert, uint16_t aCertLen)
{
Error error = Get<SettingsDriver>().Set(kKeyTcatCommrCert, aCert, aCertLen);
Log(kActionSave, error, kKeyTcatCommrCert);
SuccessOrAssert(error);
}
#endif
#if OPENTHREAD_FTD
Error Settings::AddChildInfo(const ChildInfo &aChildInfo)
{
+29 -1
View File
@@ -119,9 +119,10 @@ public:
kKeyBrUlaPrefix = OT_SETTINGS_KEY_BR_ULA_PREFIX,
kKeyBrOnLinkPrefixes = OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES,
kKeyBorderAgentId = OT_SETTINGS_KEY_BORDER_AGENT_ID,
kKeyTcatCommrCert = OT_SETTINGS_KEY_TCAT_COMMR_CERT,
};
static constexpr Key kLastKey = kKeyBorderAgentId; ///< The last (numerically) enumerator value in `Key`.
static constexpr Key kLastKey = kKeyTcatCommrCert; ///< The last (numerically) enumerator value in `Key`.
static_assert(static_cast<uint16_t>(kLastKey) < static_cast<uint16_t>(OT_SETTINGS_KEY_VENDOR_RESERVED_MIN),
"Core settings keys overlap with vendor reserved keys");
@@ -802,6 +803,33 @@ public:
*/
void DeleteOperationalDataset(MeshCoP::Dataset::Type aType);
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
/**
* Stores the Tcat Commissioner certificate.
*
* @param[in] aCert The DER-encoded X509 end-entity certificate to store.
* @param[in] aCertLen Certificate length.
*/
void SaveTcatCommissionerCertificate(uint8_t *aCert, uint16_t aCertLen);
/**
* Reads the Tcat Commissioner certificate.
*
* @param[out] aCert Buffer to store the DER-encoded X509 end-entity certificate
* of the TCAT Commissioner.
* @param[in,out] aCertLen On input, the max size of @p aCert. On output, the length of
* the DER encoded peer certificate.
*
* @retval kErrorNone Successfully read the Dataset.
* @retval kErrorNotFound No corresponding value in the setting store.
* @retval kErrorNoBufs Buffer has not enough space to store the data.
*/
Error ReadTcatCommissionerCertificate(uint8_t *aCert, uint16_t &aCertLen)
{
return Get<SettingsDriver>().Get(kKeyTcatCommrCert, aCert, &aCertLen);
}
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
/**
* Reads a specified settings entry.
*
+31
View File
@@ -1230,6 +1230,37 @@ exit:
#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
Error SecureTransport::Extension::GetPeerCertificateDer(uint8_t *aPeerCert, size_t *aCertLength, size_t aCertBufferSize)
{
Error error = kErrorNone;
SecureSession *session = mSecureTransport.mSessions.GetHead();
VerifyOrExit(session->IsConnected(), error = kErrorInvalidState);
#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
VerifyOrExit(session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.len < aCertBufferSize,
error = kErrorNoBufs);
*aCertLength = session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.len;
memcpy(aPeerCert, session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.p, *aCertLength);
#else
VerifyOrExit(
session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len) <
aCertBufferSize,
error = kErrorNoBufs);
*aCertLength =
session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len);
memcpy(aPeerCert,
session->mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
*aCertLength);
#endif
exit:
return error;
}
Error SecureTransport::Extension::GetPeerSubjectAttributeByOid(const char *aOid,
size_t aOidLength,
uint8_t *aAttributeBuffer,
+13
View File
@@ -435,6 +435,19 @@ public:
#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
/**
* Returns the DER encoded peer x509 certificate.
*
* @param[out] aPeerCert A pointer to the DER encoded certificate buffer.
* @param[out] aCertLength The length of the DER encoded peer certificate.
* @param[in] aCertBufferSize The buffer size of aPeerCert.
*
* @retval kErrorInvalidState Not connected yet.
* @retval kErrorNone Successfully get the peer certificate.
* @retval kErrorNoBufs Can't allocate memory for certificate.
*/
Error GetPeerCertificateDer(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize);
/**
* Returns an attribute value identified by its OID from the subject
* of the peer x509 certificate. The peer OID is provided in binary format.
+75 -4
View File
@@ -404,6 +404,10 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length);
break;
case kTlvGetActiveOperationalDataset:
error = HandleGetActiveOperationalDataset(aOutgoingMessage, response);
break;
case kTlvStartThreadInterface:
error = HandleStartThreadInterface();
break;
@@ -454,6 +458,9 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
case kTlvRequestPskdHash:
error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response);
break;
case kTlvGetCommissionerCertificate:
error = HandleGetCommissionerCertificate(aOutgoingMessage, response);
break;
default:
error = kErrorInvalidCommand;
}
@@ -509,33 +516,96 @@ Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessa
Dataset dataset;
OffsetRange offsetRange;
Error error;
uint8_t buf[kCommissionerCertMaxLength];
size_t bufLen = sizeof(buf);
offsetRange.Init(aOffset, aLength);
SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange));
SuccessOrExit(error = dataset.ValidateTlvs());
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
mDeviceAuthorizationField.mApplicationFlags, &dataset))
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
mDeviceAuthorizationField.mCommissioningFlags, &dataset))
{
error = kErrorRejected;
ExitNow();
}
SuccessOrExit(error = Get<Ble::BleSecure>().GetPeerCertificateDer(buf, &bufLen, bufLen));
Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
Get<ActiveDatasetManager>().SaveLocal(dataset);
exit:
return error;
}
Error TcatAgent::HandleGetActiveOperationalDataset(Message &aOutgoingMessage, bool &aResponse)
{
Error error = kErrorNone;
Dataset dataset;
Dataset::Tlvs datasetTlvs;
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
mDeviceAuthorizationField.mCommissioningFlags, &dataset))
{
error = kErrorRejected;
ExitNow();
}
SuccessOrExit(error = Get<ActiveDatasetManager>().Read(datasetTlvs));
SuccessOrExit(
error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, datasetTlvs.mTlvs, datasetTlvs.mLength));
aResponse = true;
exit:
return error;
}
Error TcatAgent::HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse)
{
Error error = kErrorNone;
Dataset dataset;
uint8_t buf[kCommissionerCertMaxLength];
uint16_t bufLen = sizeof(buf);
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
mDeviceAuthorizationField.mCommissioningFlags, &dataset))
{
error = kErrorRejected;
ExitNow();
}
VerifyOrExit(kErrorNone == Get<Settings>().ReadTcatCommissionerCertificate(buf, bufLen),
error = kErrorInvalidState);
SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, buf, bufLen));
aResponse = true;
exit:
return error;
}
Error TcatAgent::HandleDecomission(void)
{
Error error = kErrorNone;
Error error = kErrorNone;
unsigned char buf[kCommissionerCertMaxLength];
size_t bufLen = sizeof(buf);
Dataset dataset;
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags,
mDeviceAuthorizationField.mDecommissioningFlags, &dataset))
{
error = kErrorRejected;
ExitNow();
}
SuccessOrExit(error = Get<Ble::BleSecure>().GetPeerCertificateDer(buf, &bufLen, bufLen));
Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false));
Get<ActiveDatasetManager>().Clear();
Get<PendingDatasetManager>().Clear();
error = Get<Instance>().ErasePersistentInfo();
IgnoreReturnValue(Get<Instance>().ErasePersistentInfo());
#if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
{
@@ -545,6 +615,7 @@ Error TcatAgent::HandleDecomission(void)
}
#endif
exit:
return error;
}
+9 -6
View File
@@ -339,6 +339,7 @@ private:
Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage);
Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
Error HandleGetActiveOperationalDataset(Message &aOutgoingMessage, bool &aResponse);
Error HandleDecomission(void);
Error HandlePing(const Message &aIncomingMessage,
Message &aOutgoingMessage,
@@ -359,6 +360,7 @@ private:
uint16_t aLength,
bool &aResponse);
Error HandleStartThreadInterface(void);
Error HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse);
Error VerifyHash(const Message &aIncomingMessage,
uint16_t aOffset,
@@ -374,12 +376,13 @@ private:
bool CanProcessTlv(uint8_t aTlvType) const;
CommandClass GetCommandClass(uint8_t aTlvType) const;
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;
static constexpr uint16_t kInstallCodeMaxSize = 255;
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;
static constexpr uint16_t kInstallCodeMaxSize = 255;
static constexpr uint16_t kCommissionerCertMaxLength = 1024;
JoinerPskd mJoinerPskd;
const VendorInfo *mVendorInfo;
+1 -1
View File
@@ -271,7 +271,7 @@ public:
/**
* @brief Gets the Install Code Verify Status during the current session.
*
* @return TRUE The install code was correctly verfied.
* @return TRUE The install code was correctly verified.
* @return FALSE The install code was not verified.
*/
bool GetInstallCodeVerifyStatus(void) const { return mTcatAgent.GetInstallCodeVerifyStatus(); }
+5 -1
View File
@@ -200,7 +200,11 @@ private:
}
private:
static constexpr uint16_t kMaxDataSize = 255;
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
static constexpr uint16_t kMaxDataSize = 1024;
#else
static constexpr uint16_t kMaxDataSize = 256;
#endif
uint8_t mData[kMaxDataSize];
} OT_TOOL_PACKED_END;
+17
View File
@@ -37,10 +37,27 @@ send "network_name\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x06"
send "get_dataset\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x04"
send "get_comm_cert\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x06"
send "commission\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x00"
send "get_dataset\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tLEN:\t106"
expect_line "\tVALUE:\t0x0e080000000000010000000300001235060004001fffe00208ef1398c2fd504b670708fd35344133d1d73e0510fda7c771a27202e232ecd04cf934f476030f4f70656e5468726561642d633634650102c64e04105e9b9b360f80b88be2603fb0135c8d650c0402a0f7f8"
send "get_comm_cert\n"
expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD"
expect_line "\tVALUE:\t0x308201d53082017ba00302010202030e1a83300a06082a8648ce3d04030230713126302406035504030c1d5468726561642043657274696669636174696f6e20446576696365434131193017060355040a0c105468726561642047726f757020496e633112301006035504070c0953616e2052616d6f6e310b300906035504080c024341310b3009060355040613025553301e170d3234303530373039333934355a170d3234303532313039333934355a303a311f301d06035504030c1654434154204578616d706c6520436f6d6d4365727431311730150603550405130e333532332d313534332d303030313059301306072a8648ce3d020106082a8648ce3d030107034200041d9abcbe167cd7a244860a9eeb364a8a830315baca1242659d17d224475f5d96124bde30505287cc41dc018dcf53820f4c69c5e69bac504e35ac96b69fcbc2efa3393037301f0603551d230418301680145fab1b296888a1d4b431a88661e7e76659edf819301406092b0601040182df2a03040704052101010101300a06082a8648ce3d0403020348003045022077847ce3845f78f26b6f9b24ca06104705139e9e50e8dacfd6954edd6ca041bc022100d2d68718da4682313f6f890b5b6edd77c1fe0ff1f990d6eaf49966f5cb74efc6"
send "thread start\n"
expect_line "\tTYPE:\tRESPONSE_W_STATUS"
expect_line "\tVALUE:\t0x00"
+164 -141
View File
@@ -44,6 +44,8 @@ from hashlib import sha256
import hmac
import binascii
CHALLENGE_SIZE = 8
class HelpCommand(Command):
@@ -85,11 +87,15 @@ class BleCommand(Command):
if not response:
return
tlv_response = TLV.from_bytes(response)
self.process_response(tlv_response, context)
return CommandResultTLV(tlv_response)
except DataNotPrepared as err:
print('Command failed', err)
return CommandResultNone()
def process_response(self, tlv_response, context):
pass
class HelloCommand(BleCommand):
@@ -129,6 +135,51 @@ class DecommissionCommand(BleCommand):
return TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes()
class DisconnectCommand(Command):
def get_help_string(self) -> str:
return 'Disconnect client from TCAT device'
async def execute_default(self, args, context):
if 'ble_sstream' not in context or context['ble_sstream'] is None:
print("TCAT Device not connected.")
return CommandResultNone()
await context['ble_sstream'].close()
return CommandResultNone()
class ExtractDatasetCommand(BleCommand):
def get_log_string(self) -> str:
return 'Getting active dataset.'
def get_help_string(self) -> str:
return 'Get active dataset from device.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.GET_ACTIVE_DATASET.value, bytes()).to_bytes()
def process_response(self, tlv_response, context):
if tlv_response.type == TcatTLVType.RESPONSE_W_PAYLOAD.value:
dataset = ThreadDataset()
dataset.set_from_bytes(tlv_response.value)
dataset.print_content()
else:
print('Dataset extraction error.')
class GetCommissionerCertificate(BleCommand):
def get_log_string(self) -> str:
return 'Getting commissioner certificate.'
def get_help_string(self) -> str:
return 'Get commissioner certificate from device.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.GET_COMMISSIONER_CERTIFICATE.value, bytes()).to_bytes()
class GetDeviceIdCommand(BleCommand):
def get_log_string(self) -> str:
@@ -177,6 +228,89 @@ class GetNetworkNameCommand(BleCommand):
return TLV(TcatTLVType.GET_NETWORK_NAME.value, bytes()).to_bytes()
class GetPskdHash(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving peer PSKd hash.'
def get_help_string(self) -> str:
return 'Get calculated PSKd hash.'
def prepare_data(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
if bless.peer_public_key is None:
raise DataNotPrepared("Peer certificate not present.")
challenge = token_bytes(CHALLENGE_SIZE)
pskd = bytes(args[0], 'utf-8')
data = TLV(TcatTLVType.GET_PSKD_HASH.value, challenge).to_bytes()
hash = hmac.new(pskd, digestmod=sha256)
hash.update(challenge)
hash.update(bless.peer_public_key)
self.digest = hash.digest()
return data
def process_response(self, tlv_response, context):
if tlv_response.value == self.digest:
print('Requested hash is valid.')
else:
print('Requested hash is NOT valid.')
class GetRandomNumberChallenge(BleCommand):
def get_log_string(self) -> str:
return 'Retrieving random challenge.'
def get_help_string(self) -> str:
return 'Get the device random number challenge.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.GET_RANDOM_NUMBER_CHALLENGE.value, bytes()).to_bytes()
def process_response(self, tlv_response, context):
bless: BleStreamSecure = context['ble_sstream']
if tlv_response.value != None:
if len(tlv_response.value) == CHALLENGE_SIZE:
bless.peer_challenge = tlv_response.value
else:
print('Challenge format invalid.')
return CommandResultNone()
class PingCommand(Command):
def get_help_string(self) -> str:
return 'Send echo request to TCAT device.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
payload_size = 10
max_payload = 512
if len(args) > 0:
payload_size = int(args[0])
if payload_size > max_payload:
print(f'Payload size too large. Maximum supported value is {max_payload}')
return CommandResultNone()
to_send = token_bytes(payload_size)
data = TLV(TcatTLVType.PING.value, to_send).to_bytes()
elapsed_time = time()
response = await bless.send_with_resp(data)
elapsed_time = 1e3 * (time() - elapsed_time)
if not response:
return CommandResultNone()
tlv_response = TLV.from_bytes(response)
if tlv_response.value != to_send:
print("Received malformed response.")
print(f"Roundtrip time: {elapsed_time} ms")
return CommandResultTLV(tlv_response)
class PresentHash(BleCommand):
def get_log_string(self) -> str:
@@ -215,141 +349,6 @@ class PresentHash(BleCommand):
return data
class GetPskdHash(Command):
def get_log_string(self) -> str:
return 'Retrieving peer PSKd hash.'
def get_help_string(self) -> str:
return 'Get calculated PSKd hash.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print(self.get_log_string())
try:
if bless.peer_public_key is None:
print("Peer certificate not present.")
return
challenge_size = 8
challenge = token_bytes(challenge_size)
pskd = bytes(args[0], 'utf-8')
data = TLV(TcatTLVType.GET_PSKD_HASH.value, challenge).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
if tlv_response.value != None:
hash = hmac.new(pskd, digestmod=sha256)
hash.update(challenge)
hash.update(bless.peer_public_key)
digest = hash.digest()
if digest == tlv_response.value:
print('Requested hash is valid.')
else:
print('Requested hash is NOT valid.')
return CommandResultTLV(tlv_response)
except DataNotPrepared as err:
print('Command failed', err)
class GetRandomNumberChallenge(Command):
def get_log_string(self) -> str:
return 'Retrieving random challenge.'
def get_help_string(self) -> str:
return 'Get the device random number challenge.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
print(self.get_log_string())
try:
data = TLV(TcatTLVType.GET_RANDOM_NUMBER_CHALLENGE.value, bytes()).to_bytes()
response = await bless.send_with_resp(data)
if not response:
return
tlv_response = TLV.from_bytes(response)
if tlv_response.value != None:
if len(tlv_response.value) == 8:
bless.peer_challenge = tlv_response.value
else:
print('Challenge format invalid.')
return CommandResultNone()
return CommandResultTLV(tlv_response)
except DataNotPrepared as err:
print('Command failed', err)
class PingCommand(Command):
def get_help_string(self) -> str:
return 'Send echo request to TCAT device.'
async def execute_default(self, args, context):
bless: BleStreamSecure = context['ble_sstream']
payload_size = 10
max_payload = 512
if len(args) > 0:
payload_size = int(args[0])
if payload_size > max_payload:
print(f'Payload size too large. Maximum supported value is {max_payload}')
return
to_send = token_bytes(payload_size)
data = TLV(TcatTLVType.PING.value, to_send).to_bytes()
elapsed_time = time()
response = await bless.send_with_resp(data)
elapsed_time = 1e3 * (time() - elapsed_time)
if not response:
return CommandResultNone()
tlv_response = TLV.from_bytes(response)
if tlv_response.value != to_send:
print("Received malformed response.")
print(f"Roundtrip time: {elapsed_time} ms")
return CommandResultTLV(tlv_response)
class ThreadStartCommand(BleCommand):
def get_log_string(self) -> str:
return 'Enabling Thread...'
def get_help_string(self) -> str:
return 'Enable thread interface.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes()
class ThreadStopCommand(BleCommand):
def get_log_string(self) -> str:
return 'Disabling Thread...'
def get_help_string(self) -> str:
return 'Disable thread interface.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes()
class ThreadStateCommand(Command):
def __init__(self):
self._subcommands = {'start': ThreadStartCommand(), 'stop': ThreadStopCommand()}
def get_help_string(self) -> str:
return 'Manipulate state of the Thread interface of the connected device.'
async def execute_default(self, args, context):
print('Invalid usage. Provide a subcommand.')
return CommandResultNone()
class ScanCommand(Command):
def get_help_string(self) -> str:
@@ -387,14 +386,38 @@ class ScanCommand(Command):
return CommandResultNone()
class DisconnectCommand(Command):
class ThreadStartCommand(BleCommand):
def get_log_string(self) -> str:
return 'Enabling Thread...'
def get_help_string(self) -> str:
return 'Disconnect client from TCAT device'
return 'Enable thread interface.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes()
class ThreadStopCommand(BleCommand):
def get_log_string(self) -> str:
return 'Disabling Thread...'
def get_help_string(self) -> str:
return 'Disable thread interface.'
def prepare_data(self, args, context):
return TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes()
class ThreadStateCommand(Command):
def __init__(self):
self._subcommands = {'start': ThreadStartCommand(), 'stop': ThreadStopCommand()}
def get_help_string(self) -> str:
return 'Manipulate state of the Thread interface of the connected device.'
async def execute_default(self, args, context):
if 'ble_sstream' not in context or context['ble_sstream'] is None:
print("TCAT Device not connected.")
return CommandResultNone()
await context['ble_sstream'].close()
print('Invalid usage. Provide a subcommand.')
return CommandResultNone()
+6 -4
View File
@@ -30,9 +30,9 @@ import shlex
from argparse import ArgumentParser
from ble.ble_stream_secure import BleStreamSecure
from cli.base_commands import (DisconnectCommand, HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand,
GetDeviceIdCommand, GetPskdHash, GetExtPanIDCommand, GetNetworkNameCommand,
GetProvisioningUrlCommand, PingCommand, GetRandomNumberChallenge, ThreadStateCommand,
ScanCommand, PresentHash)
ExtractDatasetCommand, GetCommissionerCertificate, GetDeviceIdCommand, GetPskdHash,
GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand,
GetRandomNumberChallenge, ThreadStateCommand, ScanCommand, PresentHash)
from .tlv_commands import TlvCommand
from cli.dataset_commands import (DatasetCommand)
from dataset.dataset import ThreadDataset
@@ -57,12 +57,14 @@ class CLI:
'network_name': GetNetworkNameCommand(),
'ping': PingCommand(),
'dataset': DatasetCommand(),
'get_dataset': ExtractDatasetCommand(),
'thread': ThreadStateCommand(),
'scan': ScanCommand(),
'random_challenge': GetRandomNumberChallenge(),
'present_hash': PresentHash(),
'peer_pskd_hash': GetPskdHash(),
'tlv': TlvCommand()
'tlv': TlvCommand(),
'get_comm_cert': GetCommissionerCertificate(),
}
self._context = {
'ble_sstream': ble_sstream,
+2
View File
@@ -43,6 +43,8 @@ class TcatTLVType(Enum):
GET_RANDOM_NUMBER_CHALLENGE = 0x13
GET_PSKD_HASH = 0x14
ACTIVE_DATASET = 0x20
GET_COMMISSIONER_CERTIFICATE = 0x25
GET_ACTIVE_DATASET = 0x40
DECOMMISSION = 0x60
APPLICATION = 0x82
THREAD_START = 0x27