Firmware Upgrade¶
Firmware upgrade happens in the following steps:
- The ported application needs to implement the functions required for the firmware upgrade functionality.
- The manufacturer builds a new firmware and packages it into a
.tkmanfw
file (see Firmware Package Structure below). - The manufacturer securely uploads the firmware package to the Tapkey cloud service.
- A lock owner checks a lock for available firmware upgrades usually using the Tapkey app on her smartphone.
- The Tapkey cloud service and app filter available firmware packages for matching packages by the following criteria:
- The lock’s manufacturer ID and firmware type as returned by
tkExt_GetManufacturerId
andtkExt_GetFirmwareType
must match the package'smanufacturerId
andfwType
fields - AND the lock’s current version code as returned by
tkExt_GetFirmwareVersionInt
must be greater or equal to the value specified in the package'scompatibleFromVersion
field - AND if the new firmware's version code is lower than the currently installed version code as returned by
tkExt_GetFirmwareVersionInt
, the new firmware's version code must be greater or equal to the value, the old firmware returns viatkExt_GetFirmwareDowngradeableToVersionInt
.
- The lock’s manufacturer ID and firmware type as returned by
- The user chooses to update the lock’s firmware.
- The Tapkey cloud service encrypts and signs the firmware package for the individual lock and sends it the user’s mobile.
- The user transmits the encrypted package via BLE or NFC to the target device.
- The target application’s
tkExt_PrepareFirmwareUpload
function is called, passing the metadata supplied in the firmware package. The target application could do some preparation steps at this point. - The target application’s
tkExt_Storage_WriteFirmwareBlock
is called for individual chunks in ascending order. - If the transmission is interrupted,
tkExt_PrepareFirmwareUpload
is called again and transmission is continued after the last successfully transmitted chunk. - After the transmission is completed, the whole firmware is read back via
tkExt_Storage_ReadFirmwareBlock
in order to verify integrity. - If verification succeeded,
tkExt_ActivateNewFirmwareOnNextRestart
is called, again passing the metadata as supplied in the firmware package. The target application should now configure the system to install the new firmware on the next restart. tkExt_Reboot
is called. The target device should reboot and install the new firmware. If the firmware package was stored to a memory external to the controller where it is installed, it must be considered, that the firmware was manipulated by an attacker. The firmware content must therefore be verified before installation and again after installation before final activation via the bootloader. A cryptographic checksum contained in the metadata can be used for this purpose.- After installation, the new firmware is started.
Firmware Downgrade¶
For testing and development purposes, firmware downgrades are supported with the following restrictions:
- Downgrades are only supported to the version as returned by the previously installed version's
tkExt_GetFirmwareDowngradeableToVersionInt
. - Depending on the configuration of the lock, the lock must either be switched to owner mode for the downgrade or the corresponding owner account must be assigned the "FirmwareDowngrade" benefit in the Tapkey backend.
- The downgrade functionality might not be presented to regular users via the regular apps but might require separate tooling or activation of some hidden features.
Security Considerations¶
Tapkey takes care of securely transmitting the firmware package delivered by the lock manufacturer as .tkmanfw
file to the application of the lock to be updated. Once the tkExt_ActivateNewFirmwareOnNextRestart
function of the application to be upgraded is called, the function may rely on:
- The data passed in the function arguments, including the metadata, is correct and was not manipulated by an attacker.
- The current application’s manufacturer ID and firmware type correspond to the values of the uploaded firmware package.
- The firmware package to be installed specifies a
compatibleFromVersion
version that is lower or equal to the version code returned by the current application’stkExt_GetFirmwareVersionInt
function. - The firmware data that can be read via the application’s
tkExt_Storage_ReadFirmwareBlock
function was checked for integrity and corresponds to the data contained in the.tkmanfw
file provided by the manufacturer. - Allowing firmware downgrades is useful for testing and development purposes but can introduce security issues if the value returned by
tkExt_GetFirmwareDowngradeableToVersionInt
is not maintained correctly and allows downgrades to too low versions.
Depending on the implementation of tkExt_Storage_WriteFirmwareBlock
, the uploaded firmware is often stored in memory external to the current controller. This allows an attacker to modify the firmware content after it was verified by Tapkey, before it is installed by the boot loader. To prevent this kind of attack, the code installing the new firmware MUST ensure that the new firmware cannot get manipulated before it is activated. This implies that a cryptographic checksum MUST be persisted to a secure memory, internal to the controller in tkExt_PrepareFirmwareUpload
or tkExt_ActivateNewFirmwareOnNextRestart
. The checksum SHOULD be checked before copying the new firmware to the controller’s program memory. As the firmware could still be manipulated during the copying process, it MUST also be checked after the copying was completed.
It is not sufficient to check a signature that is contained in the firmware package to be installed and omit the check against a cryptographic checksum stored internally, as this might allow an attacker to install a firmware version that was previously published and rejected afterwards due to (security) issues. As cryptographic checksum, we suggest using SHA-256 or stronger or an asymmetric signature with the public key used for verification stored in memory internal to the controller to be updated.
If the code installing the new firmware detects an inconsistency, it should either silently restart the old firmware version. If parts of the old firmware version were already overwritten, the locking device MUST stop permanently. Even a restart MUST NOT restart the old or start the new, inconsistent firmware.
Other Considerations¶
Consider that the application to be updated is almost certainly of a different version than the firmware to be installed. The application to be updated, has any version down to the version specified in the update package’s compatibleFromVersion
field. Therefore the metadata passed with the firmware must have a formate that all these versions can handle.
During the update process, the user should be given some indication that the device being updated is busy to avoid confusion and frustration.
Firmware Package Structure¶
Firmware packages to be handled by Tapkey are packed into files in JSON format with the file name extension .tkmanfw
. The files have the following structure:
public class ManufacturerFirmwareDto
{
public class ManufacturerFirmwareInfoDto
{
/// <summary>
/// The manufacturer ID as assigned by Tapkey
/// </summary>
public int manufacturerId { get; set; }
/// <summary>
/// Together with the manufacturerId the firmware type string is used to match firmware
/// packages with individual locks. A firmware package can only be installed on devices
/// that specify the same manufacturer id and firmware type as specified here.
/// The must only contain ASCII characters and mustn't exceed a length of 8 characters.
/// </summary>
public string fwType { get; set; }
/// <summary>
/// The integer version code is used to to order firmware versions. Higher values
/// represent newer versions. Regular firmware upgrade functionality only allows the
/// installation of newer firmware versions.
/// </summary>
public uint versionCode { get; set; }
/// <summary>
/// The version code of the oldest firmware version that can directly get upgraded to
/// the current firmware version represented by this package. This value is used to
/// filter firmware packages offered for a specific device. If the target device has
/// a firmware version code lower than specified in this value, the current package
/// will not be offered for installation.
/// </summary>
public uint compatibleFromVersion { get; set; }
/// <summary>
/// A downgrade can be performed down to the specified version. Downgrading to a version
/// older than specified in this property will not be accepted by the lock, most likely
/// because the internal data structures were different before this version or the Tapkey
/// lock protocol version has changed.
/// </summary>
public uint? downgradeableToVersion { get; set; }
/// <summary>
/// The protocol version as implemented in the lock SDK used to build this firmware
/// version.
/// </summary>
public ushort? tlcpProtocolVersion { get; set; }
/// <summary>
/// The lock SDK's version code as used to build this firmware version.
/// </summary>
public uint? tlcplibVersionCode { get; set; }
/// <summary>
/// The lock SDK's build string as used to build this firmware version.
/// </summary>
public string tlcplibBuildString { get; set; }
/// <summary>
/// The version name as displayed to the user.
/// </summary>
public string versionName { get; set; }
/// <summary>
/// The build string that uniquely identifies the build that produced the current
/// binary. This might be displayed to the user in a technical details view.
/// </summary>
public string buildString { get; set; }
}
public class ManufacturerFirmwareContentDto
{
/// <summary>
/// The metadata that will be passed to the application executing the upgrade.
/// The metadata mustn't exceed a length of 128 bytes.
/// </summary>
/// <remarks>
/// The metadata can be used to carry a cryptographic checksum that allows the firmware
/// installer to verify integrity of the firmware package to install. This might be
/// implemented as SHA-256 hash or similar.
/// </remarks>
public byte[] metadata { get; set; }
/// <summary>
/// The actual firmware data to be passed to the application being upgraded. It's up to
/// the target application to interpret the content. This might be the actual binary
/// image, it might be a compressed image, etc.
/// </summary>
public byte[] firmwareData { get; set; }
}
public ManufacturerFirmwareInfoDto info { get; set; }
public ManufacturerFirmwareContentDto content { get; set; }
}
Example¶
The following listing shows the content of an exemplary MyFirmware.tkmanfw
.
{
"info": {
"manufacturerId": 1234,
"fwType": "a1",
"versionCode": 10203004,
"compatibleFromVersion": 10100000,
"downgradeableToVersion": 10200000,
"tlcpProtocolVersion": 54,
"tlcplibVersionCode": 11012123,
"tlcplibBuildString": "20160401-0223_10203004_ac810aa5",
"versionName": "1.2.3",
"buildString": "custom-build-string-usually-with-date-and-version-number",
},
"content": {
"metadata": "… BASE64 encoded byte array, max 128 bytes …",
"firmwareData": "… BASE64 encoded byte array …"
}
}