Skip to content

Commit

Permalink
[tcat] implement extraction of active dataset and commissioner cert (o…
Browse files Browse the repository at this point in the history
…penthread#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`
  • Loading branch information
canisLupus1313 authored Jan 21, 2025
1 parent 8c0363c commit 4d6def3
Show file tree
Hide file tree
Showing 16 changed files with 370 additions and 132 deletions.
20 changes: 20 additions & 0 deletions include/openthread/ble_secure.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion include/openthread/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/openthread/platform/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand Down
10 changes: 10 additions & 0 deletions src/core/api/ble_secure_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -96,6 +98,14 @@ otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char
#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,
Expand Down
17 changes: 15 additions & 2 deletions src/core/common/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand Down Expand Up @@ -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)
{
Expand Down
30 changes: 29 additions & 1 deletion src/core/common/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ class SettingsBase : public InstanceLocator
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");
Expand Down Expand Up @@ -802,6 +803,33 @@ class Settings : public SettingsBase, private NonCopyable
*/
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.
*
Expand Down
31 changes: 31 additions & 0 deletions src/core/meshcop/secure_transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,37 @@ Error SecureTransport::Extension::GetPeerCertificateBase64(unsigned char *aPeerC
#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,
Expand Down
13 changes: 13 additions & 0 deletions src/core/meshcop/secure_transport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,19 @@ class SecureTransport : private NonCopyable
#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.
Expand Down
79 changes: 75 additions & 4 deletions src/core/meshcop/tcat_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
{
Expand All @@ -545,6 +615,7 @@ Error TcatAgent::HandleDecomission(void)
}
#endif

exit:
return error;
}

Expand Down
15 changes: 9 additions & 6 deletions src/core/meshcop/tcat_agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable

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,
Expand All @@ -359,6 +360,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable
uint16_t aLength,
bool &aResponse);
Error HandleStartThreadInterface(void);
Error HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse);

Error VerifyHash(const Message &aIncomingMessage,
uint16_t aOffset,
Expand All @@ -374,12 +376,13 @@ class TcatAgent : public InstanceLocator, private NonCopyable
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;
Expand Down
2 changes: 1 addition & 1 deletion src/core/radio/ble_secure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ class BleSecure : public InstanceLocator, public MeshCoP::Tls::Extension, privat
/**
* @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(); }
Expand Down
Loading

0 comments on commit 4d6def3

Please sign in to comment.