Subkeys
Subkeys is an OP-TEE-specific implementation to provide a public key hierarchy. Subkeys can be delegated to allow different actors to sign different TAs without sharing a private key.
The first key in the chain is verified using a root key. Example Subkey hierarchy:
Each subkey defines a UUIDv5 namespace [1] to which another signed subkey or TA must belong. This avoids that one subkey can be used to sign TAs which should be signed with a different key. Since the UUIDs are restricted by this there is also a special kind of subkey, called identity subkey, which uses the same UUID as the TA it is supposed to sign.
A subkey consists of two files, a private key pair in a .pem file and the signed public key in a .bin file. Subkeys reuse the signed header (SHDR) format used for signed TAs, followed by a payload holding a public key and UUID among other fields. A subkey is formatted with all fields in little endian as:
Size in bytes |
Field Name |
Protected by field |
|
Signed header (struct shdr) |
|||
4 |
magic |
hash |
0x4f545348 |
4 |
img_type |
hash |
3, SHDR_SUBKEY |
4 |
img_size |
hash |
|
4 |
algo |
hash |
Signature algorithm, GP TEE_ALG_* |
4 |
hash_size |
hash |
|
4 |
sig_size |
hash |
|
hash_size |
hash |
sig |
|
sig_size |
sig |
previous pub key |
Root key or a subkey higher up in the chain |
Subkey header (struct shdr_subkey) |
|||
16 |
UUID |
hash |
UUIDv5 namespace for next subkey or TA |
4 |
name_size |
hash |
Size of the UUIDv5 name for the next subkey or TA, 0 for identiy subkeys |
4 |
subkey_version |
hash |
|
4 |
max_depth |
hash |
|
4 |
algo |
hash |
Signature algorithm for the next subkey or TA |
4 |
attr_count |
hash |
|
Subkey attrs * attr_count |
Attributes of the public key in this subkey |
||
4 |
id |
hash |
GP TEE_ATTR_* |
4 |
offs |
hash |
|
4 |
size |
hash |
|
opaque data padding up this binary to img_size |
hash |
The subkey attributes above point into this area using offset and size |
All subkeys included in the subkey hierarchy are added in front when a TA is signed using a subkey. For example, if a TA is signed using the second-level subkey above it would look like this:
Size in bytes |
Binary |
|
first.img_size |
First-level subkey | Signed by Root key |
|
first.name_size |
UUIDv5 name string for the next subkey |
Not signed, used to prove that the next UUID is in the namespace of the First-level subkey. This size is from “name_size” of the previous subkey (First-level). |
second.img_size |
Second-level subkey |
Signed by First-level subkey |
second.name_size |
UUIDv5 name string for the TA |
Not signed used to prove that the next UUID is in the namespace of the Second-level subkey. This size is from “name_size” of the previous subkey (Second-level). |
second.img_size |
TA |
Signed by Second-level subkey |
The signed TA binary is self-contained with all the public keys needed for verification included, except the public root key which is embedded in the TEE core binary.
The UUIDv5 name string is a separate field between subkeys and the next subkey or TA to allow a subkey to be used to sign more than one other subkey or TA.
A signed TA or subkey can be inspected using the sign_encrypt.py script, for example:
$ scripts/sign_encrypt.py display --in 5c206987-16a3-59cc-ab0f-64b9cfc9e758.ta
Subkey
struct shdr
magic: 0x4f545348
img_type: 3 (SHDR_SUBKEY)
img_size: 320 bytes
algo: 0x70414930 (TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256)
hash_size: 32 bytes
sig_size: 256 bytes
hash: f573f329fe77be686ce71647909c4ea35b5e1cd7de86369bd7d9fca31f6a4d65
struct shdr_subkey
uuid: f04fa996-148a-453c-b037-1dcfbad120a6
name_size: 64
subkey_version: 1
max_depth: 4
algo: 0x70414930 (TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256)
attr_count: 2
next name: "mid_level_subkey"
Next header at offset: 692 (0x2b4)
Subkey
struct shdr
magic: 0x4f545348
img_type: 3 (SHDR_SUBKEY)
img_size: 320 bytes
algo: 0x70414930 (TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256)
hash_size: 32 bytes
sig_size: 256 bytes
hash: 233a6dcf1a2cf69e50cde8e20c4129157da707c76fa86ce12ee31037edef02d7
struct shdr_subkey
uuid: 1a5948c5-1aa0-518c-86f4-be6f6a057b16
name_size: 64
subkey_version: 1
max_depth: 3
algo: 0x70414930 (TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256)
attr_count: 2
next name: "subkey1_ta"
Next header at offset: 1384 (0x568)
Bootstrap TA
struct shdr
magic: 0x4f545348
img_type: 1 (SHDR_BOOTSTRAP_TA)
img_size: 84576 bytes
algo: 0x70414930 (TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256)
hash_size: 32 bytes
sig_size: 256 bytes
hash: ea31ac7dc2cc06a9dc2853cd791dd00f784b5edc062ecfa274deeb66589b4ca5
struct shdr_bootstrap_ta
uuid: 5c206987-16a3-59cc-ab0f-64b9cfc9e758
ta_version: 0
TA offset: 1712 (0x6b0) bytes
TA size: 84576 (0x14a60) bytes
Footnotes