[ncp] Transforming spinel frames between AP and NCP (#2402)

* [ncp] Transforming spinel frames between AP and NCP

NCP Spinel Transformer allows to transform spinel frames sent between
Application Processor (AP) and Network Co-Processor (NCP).

Spinel frames can be transformed by using transformer library
(or libraries) implementing spinel_transformer.hpp. Libraries
can be specified using option --with-ncp-spinel-transformer-libs.
Addidionaly, transformer's outbound buffer can be changed if needed,
using option --with-ncp-spinel-transformer-outbound-buffer-size.
This commit is contained in:
Krzysztof Bogucki
2018-01-18 19:21:20 +00:00
committed by Jonathan Hui
parent c59da516ae
commit 6284d5cad6
7 changed files with 220 additions and 8 deletions
+26
View File
@@ -921,6 +921,31 @@ AC_SUBST(OPENTHREAD_NCP_VENDOR_HOOK_SOURCE)
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_NCP_VENDOR_HOOK],[${OPENTHREAD_ENABLE_NCP_VENDOR_HOOK}],[Define to 1 if using NCP vendor hook])
AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_VENDOR_HOOK], [test "${OPENTHREAD_ENABLE_NCP_VENDOR_HOOK}" = "1"])
#
# NCP Spinel Encrypter
#
AC_ARG_WITH(
[ncp-spinel-encrypter-libs],
[AS_HELP_STRING([--with-ncp-spinel-encrypter-libs=<LIBSPINEL_ENCRYPTER.A>],[Specify library files (absolute paths) implementing the NCP Spinel Encrypter. @<:@default=none@:>@.])],
[
if test "${withval}" = "no"
then OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER=0
else
OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER=1
OPENTHREAD_NCP_SPINEL_ENCRYPTER_LIBS=${withval}
fi
],
[
OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER=0
])
AC_MSG_CHECKING([for NCP Spinel Encrypter])
AC_MSG_RESULT(${OPENTHREAD_NCP_SPINEL_ENCRYPTER_LIBS-none})
AC_SUBST(OPENTHREAD_NCP_SPINEL_ENCRYPTER_LIBS)
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER],[${OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER}],[Define to 1 if using NCP Spinel Encrypter])
AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER], [test "${OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER}" = "1"])
#
# Child Supervision
#
@@ -1564,6 +1589,7 @@ AC_MSG_NOTICE([
OpenThread NCP-FTD support : ${enable_ncp_app_ftd}
OpenThread NCP-BUS Configuration : ${with_ncp_bus}
OpenThread NCP Vendor Hook Source : ${with_ncp_vendor_hook_source}
OpenThread NCP Spinel Encrypter : ${with_ncp_spinel_encrypter_libs}
OpenThread Multiple Instances support : ${enable_multiple_instances}
OpenThread MTD Network Diagnostic support : ${enable_mtd_network_diagnostic}
OpenThread builtin mbedtls support : ${enable_builtin_mbedtls}
+6
View File
@@ -63,6 +63,12 @@ LDADD_COMMON += \
$(NULL)
endif
if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
LDADD_COMMON += \
$(OPENTHREAD_NCP_SPINEL_ENCRYPTER_LIBS) \
$(NULL)
endif # OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
if OPENTHREAD_ENABLE_NCP_FTD
bin_PROGRAMS += \
ot-ncp-ftd \
+11
View File
@@ -803,6 +803,17 @@
#define OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE 1300
#endif
/**
* @def OPENTHREAD_CONFIG_NCP_SPINEL_ENCRYPTER_EXTRA_DATA_SIZE
*
* The size of extra data to be allocated in UART buffer,
* needed by NCP Spinel Encrypter.
*
*/
#ifndef OPENTHREAD_CONFIG_NCP_SPINEL_ENCRYPTER_EXTRA_DATA_SIZE
#define OPENTHREAD_CONFIG_NCP_SPINEL_ENCRYPTER_EXTRA_DATA_SIZE 0
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT
*
+86 -5
View File
@@ -105,6 +105,9 @@ NcpUart::NcpUart(Instance *aInstance):
mByte(0),
mUartSendImmediate(false),
mUartSendTask(*aInstance, EncodeAndSendToUart, this)
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
, mTxFrameBufferEncrypterReader(mTxFrameBuffer)
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
{
mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
@@ -142,8 +145,13 @@ void NcpUart::EncodeAndSendToUart(void)
{
uint16_t len;
bool prevHostPowerState;
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
NcpFrameBufferEncrypterReader &txFrameBuffer = mTxFrameBufferEncrypterReader;
#else
NcpFrameBuffer &txFrameBuffer = mTxFrameBuffer;
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
while (!mTxFrameBuffer.IsEmpty() || (mState == kFinalizingFrame))
while (!txFrameBuffer.IsEmpty() || (mState == kFinalizingFrame))
{
switch (mState)
{
@@ -157,13 +165,13 @@ void NcpUart::EncodeAndSendToUart(void)
VerifyOrExit(super_t::ShouldDeferHostSend() == false);
SuccessOrExit(mFrameEncoder.Init(mUartBuffer));
mTxFrameBuffer.OutFrameBegin();
txFrameBuffer.OutFrameBegin();
mState = kEncodingFrame;
while (!mTxFrameBuffer.OutFrameHasEnded())
while (!txFrameBuffer.OutFrameHasEnded())
{
mByte = mTxFrameBuffer.OutFrameReadByte();
mByte = txFrameBuffer.OutFrameReadByte();
case kEncodingFrame:
@@ -174,7 +182,7 @@ void NcpUart::EncodeAndSendToUart(void)
// call to OutFrameRemove.
prevHostPowerState = mHostPowerStateInProgress;
mTxFrameBuffer.OutFrameRemove();
txFrameBuffer.OutFrameRemove();
if (prevHostPowerState && !mHostPowerStateInProgress)
{
@@ -256,7 +264,15 @@ void NcpUart::HandleFrame(void *aContext, uint8_t *aBuf, uint16_t aBufLength)
void NcpUart::HandleFrame(uint8_t *aBuf, uint16_t aBufLength)
{
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
size_t dataLen = aBufLength;
if (SpinelEncrypter::DecryptInbound(aBuf, sizeof(mRxBuffer), &dataLen))
{
super_t::HandleReceive(aBuf, dataLen);
}
#else
super_t::HandleReceive(aBuf, aBufLength);
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
}
void NcpUart::HandleError(void *aContext, otError aError, uint8_t *aBuf, uint16_t aBufLength)
@@ -297,6 +313,71 @@ void NcpUart::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength)
otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf + 1), static_cast<int>(strlen(hexbuf) - 1));
}
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
NcpUart::NcpFrameBufferEncrypterReader::NcpFrameBufferEncrypterReader(NcpFrameBuffer &aTxFrameBuffer) :
mTxFrameBuffer(aTxFrameBuffer),
mDataBufferReadIndex(0),
mOutputDataLength(0) {}
bool NcpUart::NcpFrameBufferEncrypterReader::IsEmpty() const
{
return mTxFrameBuffer.IsEmpty() && !mOutputDataLength;
}
otError NcpUart::NcpFrameBufferEncrypterReader::OutFrameBegin()
{
otError status = OT_ERROR_FAILED;
Reset();
if ((status = mTxFrameBuffer.OutFrameBegin()) == OT_ERROR_NONE)
{
mOutputDataLength = mTxFrameBuffer.OutFrameGetLength();
if (mOutputDataLength > 0)
{
assert(mOutputDataLength <= sizeof(mDataBuffer));
mTxFrameBuffer.OutFrameRead(mOutputDataLength, mDataBuffer);
if (!SpinelEncrypter::EncryptOutbound(mDataBuffer, sizeof(mDataBuffer), &mOutputDataLength))
{
mOutputDataLength = 0;
status = OT_ERROR_FAILED;
}
}
else
{
status = OT_ERROR_FAILED;
}
}
return status;
}
bool NcpUart::NcpFrameBufferEncrypterReader::OutFrameHasEnded()
{
return (mDataBufferReadIndex >= mOutputDataLength);
}
uint8_t NcpUart::NcpFrameBufferEncrypterReader::OutFrameReadByte()
{
return mDataBuffer[mDataBufferReadIndex++];
}
otError NcpUart::NcpFrameBufferEncrypterReader::OutFrameRemove()
{
return mTxFrameBuffer.OutFrameRemove();
}
void NcpUart::NcpFrameBufferEncrypterReader::Reset()
{
mOutputDataLength = 0;
mDataBufferReadIndex = 0;
}
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
} // namespace Ncp
} // namespace ot
+40 -3
View File
@@ -38,6 +38,10 @@
#include "ncp/hdlc.hpp"
#include "ncp/ncp_base.hpp"
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
#include "spinel_encrypter.hpp"
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
namespace ot {
namespace Ncp {
@@ -70,9 +74,9 @@ private:
enum
{
kUartTxBufferSize = OPENTHREAD_CONFIG_NCP_UART_TX_CHUNK_SIZE, // Uart tx buffer size.
kRxBufferSize = OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE, // Rx buffer size (should be large enough to fit
// one whole (decoded) received frame).
kUartTxBufferSize = OPENTHREAD_CONFIG_NCP_UART_TX_CHUNK_SIZE, // Uart tx buffer size.
kRxBufferSize = OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE + // Rx buffer size (should be large enough to fit
OPENTHREAD_CONFIG_NCP_SPINEL_ENCRYPTER_EXTRA_DATA_SIZE, // one whole (decoded) received frame).
};
enum UartTxState
@@ -96,6 +100,35 @@ private:
uint8_t mBuffer[kUartTxBufferSize];
};
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
/**
* Wraps NcpFrameBuffer allowing to read data through spinel encrypter.
* Creates additional buffers to allow transforming of the whole spinel frames.
*/
class NcpFrameBufferEncrypterReader
{
public:
/**
* C-tor.
* Takes a reference to NcpFrameBuffer in order to read spinel frames.
*/
explicit NcpFrameBufferEncrypterReader(NcpFrameBuffer &aTxFrameBuffer);
bool IsEmpty() const;
otError OutFrameBegin();
bool OutFrameHasEnded();
uint8_t OutFrameReadByte();
otError OutFrameRemove();
private:
void Reset();
NcpFrameBuffer &mTxFrameBuffer;
uint8_t mDataBuffer[kRxBufferSize];
size_t mDataBufferReadIndex;
size_t mOutputDataLength;
};
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
void EncodeAndSendToUart(void);
void HandleFrame(uint8_t *aBuf, uint16_t aBufLength);
void HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength);
@@ -116,6 +149,10 @@ private:
uint8_t mRxBuffer[kRxBufferSize];
bool mUartSendImmediate;
Tasklet mUartSendTask;
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
NcpFrameBufferEncrypterReader mTxFrameBufferEncrypterReader;
#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
};
} // namespace Ncp
+18
View File
@@ -82,6 +82,24 @@
#define SPINEL_FRAME_MAX_SIZE 1300
/**
* @def SPINEL_ENCRYPTER_EXTRA_DATA_SIZE
*
* The size of extra data to be allocated for spinel frame buffer,
* needed by Spinel Encrypter.
*
*/
#define SPINEL_ENCRYPTER_EXTRA_DATA_SIZE 0
/**
* @def SPINEL_FRAME_BUFFER_SIZE
*
* The size of buffer large enough to fit one whole spinel frame with extra data
* needed by Spinel Encrypter.
*
*/
#define SPINEL_FRAME_BUFFER_SIZE (SPINEL_FRAME_MAX_SIZE + SPINEL_ENCRYPTER_EXTRA_DATA_SIZE)
/// Macro for generating bit masks using bit index from the spec
#define SPINEL_BIT_MASK(bit_index,field_bit_count) \
( (1 << ((field_bit_count) - 1)) >> (bit_index) )
+33
View File
@@ -0,0 +1,33 @@
/**
* Allows to encrypt spinel frames sent between Application Processor (AP) and Network Co-Processor (NCP).
*/
namespace SpinelEncrypter {
/**
* Encrypts spinel frames before sending to AP/NCP.
*
* This method encrypts outbound frames in both directions, i.e. from AP to NCP and from NCP to AP.
*
* @param[in,out] aFrameBuf Pointer to buffer containing the frame, also where the encrypted frame will be placed.
* @param[in] aFrameSize Max number of bytes in frame buffer (max length of spinel frame + additional data for encryption).
* @param[in,out] aFrameLength Pointer to store frame length, on input value is set to frame length,
* on output changed to show the frame length after encryption.
* @return \c true on success, \c false otherwise.
*/
bool EncryptOutbound(unsigned char *aFrameBuf, size_t aFrameSize, size_t *aFrameLength);
/**
* Decrypts spinel frames received from AP/NCP.
*
* This method decrypts inbound frames in both directions, i.e. from AP to NCP and from NCP to AP.
*
* @param[in,out] aFrameBuf Pointer to buffer containing encrypted frame, also where the decrypted frame will be placed.
* @param[in] aFrameSize Max number of bytes in frame buffer (max length of spinel frame + additional data for encryption).
* @param[in,out] aFrameLength Pointer to store frame length, on input value is set to encrypted frame length,
* on output changed to show the frame length after decryption.
* @return \c true on success, \c false otherwise.
*/
bool DecryptInbound(unsigned char *aFrameBuf, size_t aFrameSize, size_t *aFrameLength);
} // namespace SpinelEncrypter