Firmware Upgrade

Firmware upgrade happens in following steps:

  1. The ported application needs to implement the functions required for the firmware upgrade functionality
  2. The manufacturer builds a new firmware and packages it into a .tkmanfw file (see Firmware Package Structure below).
  3. The manufacturer securely uploads the firmware package to the Tapkey cloud service.
  4. A lock owner checks a lock for available firmware upgrades usually using the Tapkey app on her smartphone.
  5. The Tapkey cloud service and app filter available firmware packages for matching packages by following criteria:
    • The lock’s manufacturer ID and firmware type as returned by tkExt_GetManufacturerId and tkExt_GetFirmwareType must match the package's manufacturerId and fwType fields
    • AND the lock’s current version code as returned by tkExt_GetFirmwareVersionInt must be greater or equal to the valud specified in the package's compatibleFromVersion 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 via tkExt_GetFirmwareDowngradeableToVersionInt.
  6. The user chooses to update the lock’s firmware.
  7. The Tapkey cloud service encrypts and signs the firmware package for the individual lock and sends it the the user’s mobile.
  8. The user transmits the encrypted package via BLE or NFC to the target device.
  9. 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.
  10. The target application’s tkExt_Storage_WriteFirmwareBlock is called for individual chunks in ascending order.
  11. If transmission is interrupted, tkExt_PrepareFirmwareUpload is called again and transmission is continued after the last successfully transmitted chunk.
  12. After transmission is completed, the whole firmware is read back via tkExt_Storage_ReadFirmwareBlock in order to verify integrity.
  13. 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.
  14. 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 on, 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.
  15. After installation, the new firmware is started.

Firmware Downgrade

For testing and development purposes, firmware downgrades are supported with following restrictions:

  • Downgrades are only supported to the version as returned by the previously installed version's tkExt_GetFirmwareDowngradeableToVersionInt.
  • Depending on the locks configuration, 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’s tkExt_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 controller’s program memory. As the firmware could still be manipulated during the copying process, it MUST also be checked after copying was completed.

It is not sufficient to check a signature that is contained in the firmware package to be installed and omitting 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 to use 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 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 …"
  }
}