-
Notifications
You must be signed in to change notification settings - Fork 1
Zigbee Signing OTA Image
Table of Contents
Signing the OTA file is a mandatory requirement of Zigbee Smart Energy applications. For non-SE applications, it's optional. In this page, we will introduce the approach of signing the OTA image of a general Zigbee 3.0 application and verifying it on the client side.
OTA image format is described in section 11.4 of ZCL spec. We will add the signature as a tag.
![](files/ZB-Zigbee-Signing-OTA-Image/ota-file-format.png)
Gecko SDK Suite 2.7.
- Wireless Starter Kit Main Board (BRD4001)
- EFR32MG12 2400/915 MHz 19 dBm Dual Band Radio Board (BRD4164A)
NA
In this example, we will use the ECDSA-P256-SHA256 signing algorithm which is the same algorithm used in GBL signature. We will also use Simplicity Commander to generate the signing key as described in section 9.3.1 of UG266.
-
Generate the signing key pair.
commander util genkey --type ecc-p256 --privkey signing-key --pubkey signing-key.pub --tokenfile signing-keytokens.txt
-
Program the public signing key into the device as manufacture tokens.
commander flash --tokengroup znet --tokenfile signing-key-tokens.txt
-
Install Python3, then install python-ecdsa library with
pip install ecdsa
. After that, download the signing script, run the commandpython OTAFileSigner.py sign <otafile> <private signing-key>
to sign the OTA file.e.g.
python OTAFileSigner.py sign OTA4164A.ota signing-key
- Create the OTA client project per described in AN728.
- Add a custom macro "MBEDTLS_SHA256_C" to enable SHA256 in mbedtls.
- Add a folder
ecdsa
in your project and copy the following source files to it. These files can be found in the example.- btl_security_ecdsa.c
- btl_security_ecdsa.h
- ecc.c
- ecc.h
- Edit the function
emAfOtaImageSignatureVerify
in source fileota-client/ota-client-signature-verify.c
.#include "mbedtls/sha256.h" #include "ecdsa/btl_security_ecdsa.h" #define SIGNATURE_TAG_ID 0x0001 #define SIGNATURE_LEN 64 #pragma pack(push) #pragma pack(1) typedef struct { uint16_t tagID; uint32_t dataLen; }OTATagHeader; #pragma pack(pop) bool emberOTAImageIsSigned(const EmberAfOtaImageId *id) { uint32_t headerSize; uint32_t imageSize; uint32_t offset; bool has_signature = false; OTATagHeader tagHeader; uint32_t readLen; EmberAfOtaStorageStatus status; status = emAfOtaStorageGetHeaderLengthAndImageSize(id, &headerSize, &imageSize); if (0 != status) { otaPrintln("Error: get image length failed"); return false; } otaPrintln("headerSize=%d imageSize=%d", headerSize, imageSize); //The last tag must be the signature offset = headerSize; while (offset < imageSize) { status = emberAfOtaStorageReadImageDataCallback(id, offset, sizeof(OTATagHeader), (uint8_t *)&tagHeader, &readLen); if (EMBER_AF_OTA_STORAGE_SUCCESS != status) { otaPrintln("Error: read image failed at offset %d", offset); return false; } otaPrintln("offset=%d tag=%d len=%d readLen=%d", offset, tagHeader.tagID, tagHeader.dataLen, readLen); if (tagHeader.tagID == SIGNATURE_TAG_ID) { has_signature = true; } offset += sizeof(OTATagHeader) + tagHeader.dataLen; } if (has_signature && (tagHeader.tagID == SIGNATURE_TAG_ID && SIGNATURE_LEN == tagHeader.dataLen)) { return true; } else { otaPrintln("Error: image not signed or signature is not the last tag"); return false; } } EmberAfImageVerifyStatus emAfOtaImageSignatureVerify(uint16_t maxHashCalculations, const EmberAfOtaImageId* id, bool newVerification) { EmberAfOtaStorageStatus status; uint32_t imageSize; uint8_t block[64]; uint32_t readSize; int32_t dataLeftToRead; uint32_t offset = 0; uint32_t readLen; uint8_t digest[32]; uint8_t signature[64]; uint8_t i; mbedtls_sha256_context ctx; if (!emberOTAImageIsSigned(id)) { return EMBER_AF_IMAGE_BAD; } mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, 0); status = emAfOtaStorageGetHeaderLengthAndImageSize(id, NULL, // header length (don't care) &imageSize); if (0 != status) { otaPrintln("Error: get image length failed"); return EMBER_AF_IMAGE_VERIFY_ERROR; } //calculate the digest. exclude the last tag dataLeftToRead = imageSize - sizeof(OTATagHeader) - SIGNATURE_LEN; offset = 0; while (dataLeftToRead > 0) { readSize = dataLeftToRead < sizeof(block) ? dataLeftToRead : sizeof(block); if ((EMBER_AF_OTA_STORAGE_SUCCESS != emberAfOtaStorageReadImageDataCallback(id, offset, readSize, block, &readLen)) || (readSize != readLen)) { otaPrintln("Error: calculate digest fail"); return EMBER_AF_IMAGE_VERIFY_ERROR; } mbedtls_sha256_update(&ctx, block, readLen); dataLeftToRead -= readLen; offset += readLen; } mbedtls_sha256_finish(&ctx, digest); otaPrint("Digest="); for (i = 0; i < 32; i++) { otaPrint("%X", digest[i]); } otaPrintln("\r\n"); //read signature status = emAfOtaStorageGetTagDataFromImage(id, SIGNATURE_TAG_ID, signature, &readLen, sizeof(signature)); if (0 != status) { otaPrintln("Error: read signature failed"); return EMBER_AF_IMAGE_VERIFY_ERROR; } //read signing key tokTypeMfgSignedBootloaderKeyX key_x; tokTypeMfgSignedBootloaderKeyY key_y; halCommonGetMfgToken(&key_x, TOKEN_MFG_SIGNED_BOOTLOADER_KEY_X); halCommonGetMfgToken(&key_y, TOKEN_MFG_SIGNED_BOOTLOADER_KEY_Y); int32_t ret = btl_verifyEcdsaP256r1(digest, &signature[0], &signature[32], (uint8_t *)&key_x, (uint8_t *)&key_y); otaPrintln("verify result ret=%d", ret); if (0 != ret) { return EMBER_AF_IMAGE_VERIFY_ERROR; } else { return EMBER_AF_IMAGE_GOOD; } }
- Save and build.
- Sign the OTA image, then put the signed OTA image on the server
- Start OTA upgrading and check it it can upgrade successfully.
Project | Comment |
---|---|
OTA4164A.sls | OTA client project with signature support |
NA
NA
Home |
Zigbee |
Bluetooth |
ZWave |
Proprietary |
Thread |
Matter |
Hardware |
Common
All resources of this repository are released under license CC BY-NC-ND 4.0