From 6284d5cad60200db97508dbb21a646966687eac7 Mon Sep 17 00:00:00 2001 From: Krzysztof Bogucki Date: Thu, 18 Jan 2018 20:21:20 +0100 Subject: [PATCH] [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. --- configure.ac | 26 +++++++ examples/apps/ncp/Makefile.am | 6 ++ src/core/openthread-core-default-config.h | 11 +++ src/ncp/ncp_uart.cpp | 91 +++++++++++++++++++++-- src/ncp/ncp_uart.hpp | 43 ++++++++++- src/ncp/spinel.h | 18 +++++ src/ncp/spinel_encrypter.hpp | 33 ++++++++ 7 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 src/ncp/spinel_encrypter.hpp diff --git a/configure.ac b/configure.ac index d55f3dab9..a064958d7 100644 --- a/configure.ac +++ b/configure.ac @@ -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=],[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} diff --git a/examples/apps/ncp/Makefile.am b/examples/apps/ncp/Makefile.am index eb80430d0..b9556f5c9 100644 --- a/examples/apps/ncp/Makefile.am +++ b/examples/apps/ncp/Makefile.am @@ -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 \ diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index eb8aab454..08e9ab187 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -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 * diff --git a/src/ncp/ncp_uart.cpp b/src/ncp/ncp_uart.cpp index 9935232d5..af06766ac 100644 --- a/src/ncp/ncp_uart.cpp +++ b/src/ncp/ncp_uart.cpp @@ -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(hexbuf + 1), static_cast(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 diff --git a/src/ncp/ncp_uart.hpp b/src/ncp/ncp_uart.hpp index f13f618ef..6c687674b 100644 --- a/src/ncp/ncp_uart.hpp +++ b/src/ncp/ncp_uart.hpp @@ -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 diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h index a0cf1d38d..5ec52faf6 100644 --- a/src/ncp/spinel.h +++ b/src/ncp/spinel.h @@ -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) ) diff --git a/src/ncp/spinel_encrypter.hpp b/src/ncp/spinel_encrypter.hpp new file mode 100644 index 000000000..ee1ca5d0d --- /dev/null +++ b/src/ncp/spinel_encrypter.hpp @@ -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