Version Binding

In Keymaster 1, all keymaster keys were cryptographically bound to the device Root of Trust, or the verified boot key. In Keymaster 2 and above, all keys are also bound to the operating system and patch level of the system image. This ensures that an attacker who discovers a weakness in an old version of system or TEE software cannot roll a device back to the vulnerable version and use keys created with the newer version. In addition, when a key with a given version and patch level is used on a device that has been upgraded to a newer version or patch level, the key is upgraded before it can be used, and the previous version of the key invalidated. In this way, as the device is upgraded, the keys will "ratchet" forward along with the device, but any reversion of the device to a previous release will cause the keys to be unusable.

HAL Changes

To support version binding and version attestation, Android 7.1 added the tags Tag::OS_VERSION and Tag::OS_PATCHLEVEL and the methods configure and upgradeKey. The version tags are automatically added by Keymaster 2+ implementations to all newly-generated (or updated) keys. Further, any attempt to use a key that does not have an OS version or patch level matching the current system OS version or patch level, respectively, is rejected with ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION is a UINT that represents the major, minor, and sub-minor portions of an Android system version as MMmmss, where MM is the major version, mm is the minor version and ss is the sub-minor version. For example 6.1.2 would be represented as 060102.

Tag::OS_PATCHLEVEL is a UINT that represents the year and month of the last update to the system as YYYYMM, where YYYY is the four-digit year and MM is the two-digit month. For example, March 2016 would be represented as 201603.

UpgradeKey

To allow keys to be upgraded to the new OS version and patch level of the system image, Android 7.1 added the upgradeKey method to the HAL:

Keymaster 3

    upgradeKey(vec keyBlobToUpgrade, vec upgradeParams)
        generates(ErrorCode error, vec upgradedKeyBlob);

Keymaster 2

keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev,
    const keymaster_key_blob_t* key_to_upgrade,
    const keymaster_key_param_set_t* upgrade_params,
    keymaster_key_blob_t* upgraded_key);
  • dev is the device structure
  • keyBlobToUpgrade is the key which needs to be upgraded
  • upgradeParams are parameters needed to upgrade the key. These will include Tag::APPLICATION_ID and Tag::APPLICATION_DATA, which are necessary to decrypt the key blob, if they were provided during generation.
  • upgradedKeyBlob is the output parameter, used to return the new key blob.

If upgradeKey is called with a key blob that cannot be parsed or is otherwise invalid, it returns ErrorCode::INVALID_KEY_BLOB. If it is called with a key whose patch level is greater than the current system value, it returns ErrorCode::INVALID_ARGUMENT. If it is called with a key whose OS version is greater than the current system value, and the system value is non-zero, it returns ErrorCode::INVALID_ARGUMENT. OS version upgrades from non-zero to zero are allowed. In the event of errors communicating with the secure world, it returns an appropriate error value (e.g. ErrorCode::SECURE_HW_ACCESS_DENIED, ErrorCode::SECURE_HW_BUSY, etc.) Otherwise, it returns ErrorCode::OK and returns a new key blob in upgradedKeyBlob.

keyBlobToUpgrade remains valid after the upgradeKey call, and could theoretically be used again if the device were downgraded. In practice, keystore generally calls deleteKey on the keyBlobToUpgrade blob shortly after the call to upgradeKey. If keyBlobToUpgrade had tag Tag::ROLLBACK_RESISTANT, then upgradedKeyBlob should have it as well (and should be rollback resistant).

Secure configuration

Note: Keymaster 3 removed the Keymaster 2 method configure. The information previously provided to Keymaster HALs through configure is available in system properties files, and manufacturer implementations read those files during startup.

To implement version binding, the keymaster TA needs a way to securely receive the current OS version and patch level (version information), and to ensure that the information it receives strongly matches the information about the running system.

To support secure delivery of version information to the TA, an os_version field has been added to the boot image header. The boot image build script automatically populates this field. OEMs and keymaster TA implementers need to work together to modify device bootloaders to extract the version information from the boot image and pass it to the TA before the non-secure system is booted. This ensures that attackers cannot interfere with provisioning of version information to the TA.

It is also necessary to ensure that the system image has the same version information as the boot image. To that end, the configure method has been added to the keymaster HAL:

keymaster_error_t (*configure)(const struct keymaster2_device* dev,
	const keymaster_key_param_set_t* params);

The params argument contains Tag::OS_VERSION and Tag::OS_PATCHLEVEL. This method is called by keymaster2 clients after opening the HAL, but before calling any other methods. If any other method is called before configure, the TA returns ErrorCode::KEYMASTER_NOT_CONFIGURED.

The first time configure is called after the device boots, it should verify that the version information provided matches what was provided by the bootloader. If the version information does not match, configure returns ErrorCode::INVALID_ARGUMENT, and all other keymaster methods continue returning ErrorCode::KEYMASTER_NOT_CONFIGURED. If the information matches, configure returns ErrorCode::OK, and other keymaster methods begin functioning normally.

Subsequent calls to configure return the same value returned by the first call, and do not change the state of keymaster. Note that this process will REQUIRErequire that all OTAs update both system and boot images; they can't be updated separately in order to keep the version information in sync.

Because configure will be called by the system whose contents it is intended to validate, there is a narrow window of opportunity for an attacker to compromise the system image and force it to provide version information that matches the boot image, but which is not the actual version of the system. The combination of boot image verification, dm-verity validation of the system image contents, and the fact that configure is called very early in the system boot should make this window of opportunity difficult to exploit.