以A/B system为例:
VB1.0
edk2/abl/QcomModulePkg/Library/avb/VerifiedBoot.c
STATIC EFI_STATUS LoadImageAndAuthVB1 (BootInfo *Info)
{
......
Status = Info->VbIntf->VBDeviceInit (Info->VbIntf,
(device_info_vb_t *)&DevInfo_vb);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error during VBDeviceInit: %r\n", Status));
return Status;
}
AsciiStrnCpyS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), "/",
AsciiStrLen ("/"));
UnicodeStrToAsciiStr (Info->Pname, PnameAscii);
if (Info->MultiSlotBoot) {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii) - (MAX_SLOT_SUFFIX_SZ - 1));
} else {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii));
}
Status =
Info->VbIntf->VBVerifyImage (Info->VbIntf, (UINT8 *)StrPnameAscii,
(UINT8 *)Info->Images[0].ImageBuffer,
Info->Images[0].ImageSize, &Info->BootState);
if (Status != EFI_SUCCESS || Info->BootState == BOOT_STATE_MAX) {
DEBUG ((EFI_D_ERROR, "VBVerifyImage failed with: %r\n", Status));
return Status;
}
.......
}
VB1在normal boot时bootloader会去验证boot.img,验证pass加载启动,看下高通的实现:
boot_images/QcomPkg/Drivers/VerifiedBootDxe/VerifiedBootDxe.c
EFI_STATUS
QCOM_VB_VerifyImage(IN QCOM_VERIFIEDBOOT_PROTOCOL *This,
IN UINT8 pname[MAX_PNAME_LENGTH], IN UINT8 *img,
IN UINT32 img_len, OUT boot_state_t *bootstate)
{
EFI_STATUS status = EFI_FAILURE;
struct boot_img_hdr *img_hdr = NULL;
VB_HASH hash_algorithm = VB_SHA256;
UINTN hash_size = VB_SHA256_SIZE;
UINT8 img_hash[hash_size];
UINT32 startTime = 0;
UINT32 endTime = 0;
startTime = GetTimerCountms();
if (!is_device_init_called) {
dev_boot_state = BOOT_STATE_MAX;
goto exit;
}
if (!pname || !img || !bootstate || img_len == 0 || img_len > MAX_UINT64 - (UINT64)img) {
dev_boot_state = BOOT_STATE_MAX;
status = EFI_INVALID_PARAMETER;
goto exit;
}
/* Skip verification in case the dev_boot_state (initialized by
* QCOM_VB_DeviceInit API) is Orange */
if (dev_boot_state == ORANGE) {
status = EFI_SUCCESS;
goto exit;
}
/* Read Android Verified Boot Signature from the image */
if (vb_read_embedded_vb_signature(pEfiQcomASN1X509Protocol, img, img_len, &embedded_vb_signature) != EFI_SUCCESS) {
dev_boot_state = RED;
status = EFI_SUCCESS;
goto exit;
}
/* Read OEM certificate from the embedded header file */
if (vb_read_oem_certificate(pEfiQcomASN1X509Protocol, &oem_certificate) != EFI_SUCCESS) {
dev_boot_state = RED;
status = EFI_SUCCESS;
goto exit;
}
/* Calculate the hash of image */
if (vb_get_image_hash(pEfiQcomASN1X509Protocol, pname, img, img_len, &embedded_vb_signature, img_hash,
hash_size, hash_algorithm) != EFI_SUCCESS) {
dev_boot_state = RED;
status = EFI_SUCCESS;
goto exit;
}
/* Verify the hash of image with OEM key */
if (vb_verify_hash_oem_certificate(pEfiQcomASN1X509Protocol, img_hash, hash_algorithm, &oem_certificate,
&embedded_vb_signature) != EFI_SUCCESS) {
dev_boot_state = YELLOW;
/* Verify the hash of image with embedded key */
if (vb_verify_hash_embedded_key(pEfiQcomASN1X509Protocol, img_hash, hash_algorithm,
&embedded_vb_signature) != EFI_SUCCESS) {
dev_boot_state = RED;
status = EFI_SUCCESS;
goto exit;
}
cert_verification_passed = TRUE;
} else {
dev_boot_state = GREEN;
}
status = EFI_SUCCESS;
/*Struct def for boot image header*/
typedef struct boot_img_hdr
{
CHAR8 magic[8];
UINT32 kernel_size; /* size in bytes */
UINT32 kernel_addr; /* physical load addr */
UINT32 ramdisk_size; /* size in bytes */
UINT32 ramdisk_addr; /* physical load addr */
UINT32 second_size; /* size in bytes */
UINT32 second_addr; /* physical load addr */
UINT32 tags_addr; /* physical addr for kernel tags */
UINT32 page_size; /* flash page size we assume */
UINT32 dt_size; /* device_tree in bytes */
UINT32 os_version; /* version << 11 | patch_level */
UINT8 name[16]; /* ascii product name */
UINT8 cmdline[512];
UINT32 id[8]; /* timestamp / checksum / sha1 / etc */
}boot_img_hdr;
可以清楚的看到,整个过程是这样的,
1、从img(boot)获取vb的Signature
2、读取OEM的certificate,各家不同
3、计算img(boot)的hash,当然根据不同的hash_algorithm
4、然后使用OEM certificate验证hash是否匹配
如果大家对4中如何验证hash好奇的话,可以参考:
EFI_STATUS VerifySignature(
CONST UINT8 *signature_ptr,
UINT32 signature_len,
UINT8 *hash,
VB_HASH hash_algorithm,
CONST UINT8 *modulus,
UINT32 modulus_len,
CONST UINT8 *public_exp,
UINT32 public_exp_len)
{
EFI_STATUS status = EFI_FAILURE;
CE_RSA_KEY key;
BigInt modulus_bi;
BigInt public_exp_bi;
INT32 hashidx;
INT32 hash_len;
UINT32 padding_type;
VOID *padding_info = NULL;
QCOM_SECRSA_PROTOCOL *pEfiQcomSecRSAProtocol = NULL;
SetMem(&key, sizeof(CE_RSA_KEY), 0);
switch (hash_algorithm) {
case VB_SHA256:
hashidx = CE_HASH_IDX_SHA256;
hash_len = VB_SHA256_SIZE;
break;
default:
DEBUG((EFI_D_ERROR, "VB: VerifySignature: Hash algorithm not supported\n"));
status = EFI_UNSUPPORTED;
goto exit;
}
key.N = AllocatePool(sizeof(S_BIGINT));
if (key.N == NULL) {
DEBUG((EFI_D_ERROR, "VB: VerifySignature: mem allocation err for key.N\n"));
goto exit;
}
key.e = AllocatePool(sizeof(S_BIGINT));
if (key.e == NULL) {
DEBUG((EFI_D_ERROR, "VB: VerifySignature: mem allocation err for key.e\n"));
goto exit;
}
status = gBS->LocateProtocol(&gEfiQcomSecRSAProtocolGuid, NULL, (VOID **) &pEfiQcomSecRSAProtocol);
if ( status != EFI_SUCCESS)
{
DEBUG((EFI_D_ERROR, "VB: VerifySignature: LocateProtocol failed, status = %x\n", status));
goto exit;
}
status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin(pEfiQcomSecRSAProtocol, modulus, modulus_len, &modulus_bi);
if ( status != EFI_SUCCESS)
{
DEBUG((EFI_D_ERROR, "VB: VerifySignature: SecRSABigIntReadBin for modulus failed!, ret = %x\n", status));
goto exit;
}
status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin(pEfiQcomSecRSAProtocol, public_exp, public_exp_len, &public_exp_bi);
if ( status != EFI_SUCCESS)
{
DEBUG((EFI_D_ERROR, "VB: VerifySignature: SecRSABigIntReadBin for modulus failed!, ret = %x\n", status));
goto exit;
}
key.N->bi = modulus_bi;
key.e->bi = public_exp_bi;
key.e->sign = S_BIGINT_POS;
key.type = CE_RSA_KEY_PUBLIC;
padding_type = CE_RSA_PAD_PKCS1_V1_5_SIG;
status = pEfiQcomSecRSAProtocol->SecRSAVerifySig(pEfiQcomSecRSAProtocol, &key, padding_type, padding_info, hashidx,hash, hash_len, (UINT8*)signature_ptr, signature_len);
if (status != EFI_SUCCESS) {
goto exit;
}
status = EFI_SUCCESS;
简单来说就是RSA公钥验签名,参考:RSA - 原理、特点(加解密及签名验签)及公钥和私钥的生成
关于kernel如何通过cmdline传来的verity key验证system分区,参考:system分区签名校验方法
简化如下,system.img细分system data / verity-metadata.img / verity.img,
分别数据内容是 system data / (dm-verity table) + (signature) / hash tree。
hash tree是将system data以4k大小划分,使用sha256生成的一颗描述system data的树;
dm-verity table是调用build_verity_metadata.py生成描述改hashtree的metadata,如果verity key
验证system signature pass,kernel会使用这个dm-verity table通过dm-verity驱动创建dm-verity块设备;
signature就是使用非对称加密算法生成对dm-verity table的数字签名;
具体的path可以参考:
system/extras/verity
system/core/fs_mgr
VB2.0
vb1.0使用OEM key验证boot分区,使用verity key验证system/vendor分区,但vb2.0使用OEM key验证vbmeta.img,并使用其中包含的public key验证其他分区(system/vendor/boot等)。Android O以后boot分区只放了kernel,bootloader需要用公钥校验vbmeta的签名,以及boot签名和system签名,签名验证通过之后才允许下一步booting,校验签名完成之后,会启动kernel,并把system分区中的rootfs相关的dm-verity数据通过cmdline的形式传递给kernel,kernel启动会挂载rootfs(注意rootfs中包含了system,两者为同一个分区),所以可以认为system的挂载是在kernel中完成的,并且使用dm-verity模式进行的挂载。
edk2/abl/QcomModulePkg/Library/avb/libavb/avb_vbmeta_image.h
* The vbmeta image consists of three blocks:
*
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | Authentication data - variable size |
* +-----------------------------------------+
* | Auxiliary data - variable size |
* +-----------------------------------------+
*
* The "Header data" block is described by this struct and is always
* |AVB_VBMETA_IMAGE_HEADER_SIZE| bytes long.
*
* The "Authentication data" block is |authentication_data_block_size|
* bytes long and contains the hash and signature used to authenticate
* the vbmeta image. The type of the hash and signature is defined by
* the |algorithm_type| field.
*
* The "Auxiliary data" is |auxiliary_data_block_size| bytes long and
* contains the auxiliary data including the public key used to make
* the signature and descriptors.
*
typedef struct AvbVBMetaImageHeader {
/* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */
uint8_t magic[AVB_MAGIC_LEN];
/* 4: The major version of libavb required for this header. */
uint32_t required_libavb_version_major;
/* 8: The minor version of libavb required for this header. */
uint32_t required_libavb_version_minor;
/* 12: The size of the signature block. */
uint64_t authentication_data_block_size;
/* 20: The size of the auxiliary data block. */
uint64_t auxiliary_data_block_size;
/* 28: The verification algorithm used, see |AvbAlgorithmType| enum. */
uint32_t algorithm_type;
/* 32: Offset into the "Authentication data" block of hash data. */
uint64_t hash_offset;
/* 40: Length of the hash data. */
uint64_t hash_size;
/* 48: Offset into the "Authentication data" block of signature data. */
uint64_t signature_offset;
/* 56: Length of the signature data. */
uint64_t signature_size;
/* 64: Offset into the "Auxiliary data" block of public key data. */
uint64_t public_key_offset;
/* 72: Length of the public key data. */
uint64_t public_key_size;
/* 80: Offset into the "Auxiliary data" block of public key metadata. */
uint64_t public_key_metadata_offset;
/* 88: Length of the public key metadata. Must be set to zero if there
* is no public key metadata.
*/
uint64_t public_key_metadata_size;
/* 96: Offset into the "Auxiliary data" block of descriptor data. */
uint64_t descriptors_offset;
/* 104: Length of descriptor data. */
uint64_t descriptors_size;
/* 112: The rollback index which can be used to prevent rollback to
* older versions.
*/
uint64_t rollback_index;
/* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be
* set to zero if the vbmeta image is not a top-level image.
*/
uint32_t flags;
/* 124: Reserved to ensure |release_string| start on a 16-byte
* boundary. Must be set to zeroes.
*/
uint8_t reserved0[4];
/* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or
* "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
* terminated. Applications must not make assumptions about how this
* string is formatted.
*/
uint8_t release_string[AVB_RELEASE_STRING_SIZE];
/* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
* bytes. This must be set to zeroes.
*/
uint8_t reserved[80];
} AVB_ATTR_PACKED AvbVBMetaImageHeader;
从上面可以得到header的大小为256byte,当然如果用avbtool也可以得到vbmeta的信息:
Minimum libavb version: 1.0
Header Block: 256 bytes
Authentication Block: XXX bytes
Auxiliary Block: XXX bytes
Algorithm: SHA256_RSA2048
Rollback Index: 0
Flags: 0
Release String: 'avbtool 1.X.X'
Descriptors:
Hash descriptor:
Image Size:
Hash Algorithm:
Partition Name:
Salt:
Digest:
Flags:
Hashtree descriptor:
Version of dm-verity:
Image Size:
Tree Offset:
Tree Size:
Data Block Size:
Hash Block Size:
FEC num roots:
FEC offset:
FEC size:
Hash Algorithm:
Android O 引入了 dtbo 和 vendor.img。这些 image 挨个校验可以说费时费力,而 AVB 2.0 的做法事实上十分简单,引入一个新的分区:vbmeta.img(verified boot metadata),然后把所有需要校验的内容在编译时就计算好打包到这个分区,那么启动过程中 BootLoader 只需要校验 vbmeta.img,就能确认 vbmeta 内的数据是否可信。再用 vbmeta 中的数据去比对 bootimg,dtbo,system,img,vendor.img 即可
Authentication data block 包含两部分内容:Hash data 和 signature data,
def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
"""Checks that the signature in a vbmeta blob was made by
the embedded public key.
Arguments:
vbmeta_header: A AvbVBMetaHeader.
vbmeta_blob: The whole vbmeta blob, including the header.
Returns:
True if the signature is valid and corresponds to the embedded
public key. Also returns True if the vbmeta blob is not signed.
"""
(_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
if alg.hash_name == '':
return True
header_blob = vbmeta_blob[0:256]
auth_offset = 256
aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
aux_size = vbmeta_header.auxiliary_data_block_size
aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
pubkey_offset = aux_offset + vbmeta_header.public_key_offset
pubkey_size = vbmeta_header.public_key_size
pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
digest_offset = auth_offset + vbmeta_header.hash_offset
digest_size = vbmeta_header.hash_size
digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
sig_offset = auth_offset + vbmeta_header.signature_offset
sig_size = vbmeta_header.signature_size
sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
# Now that we've got the stored digest, public key, and signature
# all we need to do is to verify. This is the exactly the same
# steps as performed in the avb_vbmeta_image_verify() function in
# libavb/avb_vbmeta_image.c.
ha = hashlib.new(alg.hash_name)
ha.update(header_blob)
ha.update(aux_blob)
Auxiliary data block包含两部分内容:AvbDescriptor 和 RSA public key,对应上面的vbmeta可以清晰的看出
它其中所包含的信息。
typedef struct AvbDescriptor {
uint64_t tag;
uint64_t num_bytes_following;
} AVB_ATTR_PACKED AvbDescriptor;
typedef enum {
AVB_DESCRIPTOR_TAG_PROPERTY,
AVB_DESCRIPTOR_TAG_HASHTREE,
AVB_DESCRIPTOR_TAG_HASH,
AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
} AvbDescriptorTag;
typedef struct AvbHashDescriptor {
AvbDescriptor parent_descriptor;
uint64_t image_size;
uint8_t hash_algorithm[32];
uint32_t partition_name_len;
uint32_t salt_len;
uint32_t digest_len;
uint8_t reserved[64];
} AVB_ATTR_PACKED AvbHashDescriptor;
参考:
Android verified boot 2.0 vbmeta 数据结构解析
Android P中的AVB校验