赞
踩
我们以一下空的dtbo.img镜像为例,进行说明
调用external/avb/avbtool.py脚本的add_hash_footer 函数
@build/core/Makefile
# dtbo image
INSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img
$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)
cp $(BOARD_PREBUILT_DTBOIMAGE) $@
$(AVBTOOL) add_hash_footer \
--image $@ \
--partition_size $(BOARD_DTBOIMG_PARTITION_SIZE) \
--partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \
$(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)
下面是avbtoo.py脚本的add_hash_footer的函数部分内容:
def add_hash_footer(self, image_filename, partition_size, partition_name...) #镜像大小 original_image_size = image.image_size digest_size = len(hashlib.new(name=hash_algorithm).digest()) #salt 加盐值是随机数,不是真随机数 软件生成的 salt = open('/dev/urandom').read(hash_size) #实例化hasher hasher = hashlib.new(name=hash_algorithm, string=salt) image.seek(0) #可以看到digest是根据镜像的大小来生成的摘要 hasher.update(image.read(image.image_size)) #生成digest值 digest = hasher.digest() #生成descriptor描述信息数据 h_desc = AvbHashDescriptor() h_desc.image_size = image.image_size h_desc.hash_algorithm = hash_algorithm h_desc.partition_name = partition_name h_desc.salt = salt h_desc.flags = 0 #生成blob数据并写入blob数据 vbmeta_blob = self._generate_vbmeta_blob(...) output_vbmeta_image.write(vbmeta_blob) vbmeta_offset = image.image_size #padding填充,padding的方式有好几种,主要目的是提高RSA的安全性。涉及到RSA的补位算法 有兴趣的自己查查看 padding_needed = ( round_to_multiple(len(vbmeta_blob), image.block_size) - len(vbmeta_blob)) vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed image.append_raw(vbmeta_blob_with_padding) vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding) image.append_dont_care(partition_size - vbmeta_end_offset - 1*image.block_size)
重点看一下_generate_vbmeta_blob函数,注释中也解释了各个步骤
def _generate_vbmeta_blob(self, algorithm_name, key_path...) “”“ This blob contains the header (struct AvbVBMetaHeader), the authentication data block (which contains the hash and signature for the header and auxiliary block), and the auxiliary block (which contains descriptors, the public key used, and other data). ”“” # Add descriptors from other images. if include_descriptors_from_image: descriptors_dict = dict() for image in include_descriptors_from_image: image_handler = ImageHandler(image.name) (_, image_vbmeta_header, image_descriptors, _) = self._parse_image( image_handler) # Bump the required libavb version to support all included descriptors. h.bump_required_libavb_version_minor( image_vbmeta_header.required_libavb_version_minor) for desc in image_descriptors: if hasattr(desc, 'partition_name'): key = type(desc).__name__ + '_' + desc.partition_name descriptors_dict[key] = desc.encode() else: encoded_descriptors.extend(desc.encode()) for key in sorted(descriptors_dict): encoded_descriptors.extend(descriptors_dict[key]) # Load public key metadata blob, if requested. pkmd_blob = [] if public_key_metadata_path: with open(public_key_metadata_path) as f: pkmd_blob = f.read() key = None encoded_key = bytearray() if alg.public_key_num_bytes > 0: if not key_path: raise AvbError('Key is required for algorithm {}'.format( algorithm_name)) encoded_key = encode_rsa_key(key_path) if len(encoded_key) != alg.public_key_num_bytes: raise AvbError('Key is wrong size for algorithm {}'.format( algorithm_name)) # For the Auxiliary data block, descriptors are stored at offset 0, # followed by the public key, followed by the public key metadata blob. h.auxiliary_data_block_size = round_to_multiple( len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64) h.descriptors_offset = 0 h.descriptors_size = len(encoded_descriptors) h.public_key_offset = h.descriptors_size h.public_key_size = len(encoded_key) h.public_key_metadata_offset = h.public_key_offset + h.public_key_size h.public_key_metadata_size = len(pkmd_blob) # For the Authentication data block, the hash is first and then # the signature. h.authentication_data_block_size = round_to_multiple( alg.hash_num_bytes + alg.signature_num_bytes, 64) h.algorithm_type = alg.algorithm_type h.hash_offset = 0 h.hash_size = alg.hash_num_bytes # Signature offset and size - it's stored right after the hash # (in Authentication data block). h.signature_offset = alg.hash_num_bytes h.signature_size = alg.signature_num_bytes # Generate Auxiliary data block. aux_data_blob = bytearray() aux_data_blob.extend(encoded_descriptors) aux_data_blob.extend(encoded_key) aux_data_blob.extend(pkmd_blob) padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob) aux_data_blob.extend('\0' * padding_bytes) # Calculate the hash. binary_hash = bytearray() binary_signature = bytearray() if algorithm_name != 'NONE': ha = hashlib.new(alg.hash_name) ha.update(header_data_blob) ha.update(aux_data_blob) binary_hash.extend(ha.digest()) # Calculate the signature. padding_and_hash = str(bytearray(alg.padding)) + binary_hash binary_signature.extend(raw_sign(signing_helper, signing_helper_with_files, algorithm_name, alg.signature_num_bytes, key_path, padding_and_hash)) # Generate Authentication data block. auth_data_blob = bytearray() auth_data_blob.extend(binary_hash) auth_data_blob.extend(binary_signature) padding_bytes = h.authentication_data_block_size - len(auth_data_blob) auth_data_blob.extend('\0' * padding_bytes)
我补上一张图,不然难理解上面脚本的验证数据和辅助数据的区别,分类如下:
下面是xxd打开dtbo.img的内容
00000ff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001000: 4156 4230 0000 0001 0000 0000 0000 0000 AVB0............ 00001010: 0000 0000 0000 0000 0000 0180 0000 0000 ................ 00001020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001040: 0000 0000 0000 0160 0000 0000 0000 0000 .......`........ 00001050: 0000 0000 0000 0160 0000 0000 0000 0000 .......`........ 00001060: 0000 0000 0000 0000 0000 0000 0000 0160 ...............` 00001070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001080: 6176 6274 6f6f 6c20 312e 312e 3000 0000 avbtool 1.1.0... 00001090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000010f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001100: 0000 0000 0000 0002 0000 0000 0000 00b8 ................ 00001110: 0000 0000 0000 0020 7368 6132 3536 0000 ....... sha256.. 00001120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001130: 0000 0000 0000 0000 0000 0004 0000 0020 ............... 00001140: 0000 0020 0000 0000 0000 0000 0000 0000 ... ............ 00001150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001160: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001170: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00001180: 0000 0000 6474 626f d720 08a9 3668 fa34 ....dtbo. ..6h.4 00001190: 1fa1 9229 5be3 51fb a68d ad00 47e6 73bb ...)[.Q.....G.s. 000011a0: 3b68 3f26 337d 2c5c d886 4242 361c 1dbd ;h?&3},\..BB6... 000011b0: 60cb c00c da36 0da6 ecad 843a bc0a f79e `....6.....:....
使用avbtool.py脚本dump一下vbmeta.img和dtbo.img,我这里编译的是一个空的dtbo.img,dump的结果如下,可以看到vbmeta和dtbo中的相关salt和digest是匹配的。
vbmeta.img内容
Minimum libavb version: 1.0 Header Block: 256 bytes Authentication Block: 576 bytes Auxiliary Block: 3456 bytes Public key (sha1): xx Algorithm: SHA256_RSA4096 Rollback Index: 0 Flags: 0 Release String: 'avbtool 1.1.0' Hash descriptor: Image Size: 32 bytes Hash Algorithm: sha256 Partition Name: dtbo Salt: d72008a93668fa341fa192295be351fba68dad0047e673bb3b683f26337d2c5c Digest: d8864242361c1dbd60cbc00cda360da6ecad843abc0af79e1da42b09bbee8922 Flags: 0
dtbo.img内容
Footer version: 1.0
...
Release String: 'avbtool 1.1.0'
Descriptors:
Hash descriptor:
Image Size: 32 bytes
Hash Algorithm: sha256
Partition Name: dtbo
Salt: d72008a93668fa341fa192295be351fba68dad0047e673bb3b683f26337d2c5c
Digest: d8864242361c1dbd60cbc00cda360da6ecad843abc0af79e1da42b09bbee8922
Flags: 0
不是所有的镜像中都有Authentication Block的,所以我们重点分析vbmeta镜像中的Authentication Block和Auxiliary Block的校验,我们衔接上篇博客“AVB2.0(四)libavb库介绍”最后尾部遗留的内容,分析一下验证签名和比对hash这两个部分。
代码在avb_slot_verify.c
avb_slot_verify -> load_and_verify_vbmeta -> avb_vbmeta_image_verify,接着这里开始分析,一共比较两个部分,先对比hash,如果hash不相等 返回AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH错误
接着验证签名,如果签名不匹配,返回AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH错误
switch (h.algorithm_type) { /* Explicit fall-through: */ case AVB_ALGORITHM_TYPE_SHA256_RSA2048: case AVB_ALGORITHM_TYPE_SHA256_RSA4096: case AVB_ALGORITHM_TYPE_SHA256_RSA8192: avb_sha256_init(&sha256_ctx); avb_sha256_update( &sha256_ctx, header_block, sizeof(AvbVBMetaImageHeader)); avb_sha256_update( &sha256_ctx, auxiliary_block, h.auxiliary_data_block_size); computed_hash = avb_sha256_final(&sha256_ctx); ##使用sha256计算得到的hash,和vbmeta头中的hash比较是否相等 ##auxiliary_block中有public key公钥数据 if (avb_safe_memcmp(authentication_block + h.hash_offset, computed_hash, h.hash_size) != 0) { avb_error("Hash does not match!\n"); ret = AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH; goto out; } #验证一下签名文件是否合法 verification_result = avb_rsa_verify(auxiliary_block + h.public_key_offset, h.public_key_size, authentication_block + h.signature_offset, h.signature_size, authentication_block + h.hash_offset, h.hash_size, algorithm->padding, algorithm->padding_len); if (verification_result == 0) { ret = AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH; goto out; }
上面步骤中有两个动作,一个是计算hash,一个是验证签名。
AvbSHA256Ctx结构体
/* Data structure used for SHA-256. */
typedef struct {
uint32_t h[8];
uint32_t tot_len;
uint32_t len;
uint8_t block[2 * AVB_SHA256_BLOCK_SIZE];
uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
void *user_data;
} AvbSHA256Ctx;
avb_sha256_init函数,主要是给h成员赋值,为下面的avb_sha256_update和avb_sha256_final提供上下文环境
/* SHA-256 implementation */ void avb_sha256_init(AvbSHA256Ctx* ctx) { #ifndef UNROLL_LOOPS int i; for (i = 0; i < 8; i++) { ctx->h[i] = sha256_h0[i]; } #else ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1]; ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3]; ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5]; ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7]; #endif /* !UNROLL_LOOPS */ ctx->len = 0; ctx->tot_len = 0; } static const uint32_t sha256_h0[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
avb_sha256_update函数
void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len) { size_t block_nb; size_t new_len, rem_len, tmp_len; const uint8_t* shifted_data; tmp_len = AVB_SHA256_BLOCK_SIZE - ctx->len; rem_len = len < tmp_len ? len : tmp_len; avb_memcpy(&ctx->block[ctx->len], data, rem_len); if (ctx->len + len < AVB_SHA256_BLOCK_SIZE) { ctx->len += len; return; } new_len = len - rem_len; block_nb = new_len / AVB_SHA256_BLOCK_SIZE; shifted_data = data + rem_len; SHA256_transform(ctx, ctx->block, 1); SHA256_transform(ctx, shifted_data, block_nb); rem_len = new_len % AVB_SHA256_BLOCK_SIZE; avb_memcpy(ctx->block, &shifted_data[block_nb << 6], rem_len); ctx->len = rem_len; ctx->tot_len += (block_nb + 1) << 6; } static void SHA256_transform(AvbSHA256Ctx* ctx, const uint8_t* message, size_t block_nb) { uint32_t w[64]; uint32_t wv[8]; uint32_t t1, t2; const unsigned char* sub_block; size_t i; #ifndef UNROLL_LOOPS size_t j; #endif for (i = 0; i < block_nb; i++) { sub_block = message + (i << 6); #ifndef UNROLL_LOOPS for (j = 0; j < 16; j++) { PACK32(&sub_block[j << 2], &w[j]); } for (j = 16; j < 64; j++) { SHA256_SCR(j); } for (j = 0; j < 8; j++) { wv[j] = ctx->h[j]; } for (j = 0; j < 64; j++) { t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); wv[7] = wv[6]; wv[6] = wv[5]; wv[5] = wv[4]; wv[4] = wv[3] + t1; wv[3] = wv[2]; wv[2] = wv[1]; wv[1] = wv[0]; wv[0] = t1 + t2; } for (j = 0; j < 8; j++) { ctx->h[j] += wv[j]; } #else PACK32(&sub_block[0], &w[0]); PACK32(&sub_block[4], &w[1]); ... PACK32(&sub_block[60], &w[15]); SHA256_SCR(16); ... SHA256_SCR(62); SHA256_SCR(63); wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 0); SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 1); ... SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 62); SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 63); ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; #endif /* !UNROLL_LOOPS */ } }
avb_sha256_final函数
uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) { size_t block_nb; size_t pm_len; uint64_t len_b; #ifndef UNROLL_LOOPS size_t i; #endif block_nb = (1 + ((AVB_SHA256_BLOCK_SIZE - 9) < (ctx->len % AVB_SHA256_BLOCK_SIZE))); len_b = (ctx->tot_len + ctx->len) << 3; pm_len = block_nb << 6; avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len); ctx->block[ctx->len] = 0x80; UNPACK64(len_b, ctx->block + pm_len - 8); SHA256_transform(ctx, ctx->block, block_nb); #ifndef UNROLL_LOOPS for (i = 0; i < 8; i++) { UNPACK32(ctx->h[i], &ctx->buf[i << 2]); } #else UNPACK32(ctx->h[0], &ctx->buf[0]); UNPACK32(ctx->h[1], &ctx->buf[4]); UNPACK32(ctx->h[2], &ctx->buf[8]); UNPACK32(ctx->h[3], &ctx->buf[12]); UNPACK32(ctx->h[4], &ctx->buf[16]); UNPACK32(ctx->h[5], &ctx->buf[20]); UNPACK32(ctx->h[6], &ctx->buf[24]); UNPACK32(ctx->h[7], &ctx->buf[28]); #endif /* !UNROLL_LOOPS */ return ctx->buf; }
接着分析avb_rsa_verify验证签名函数
/* Verify a RSA PKCS1.5 signature against an expected hash. * Returns false on failure, true on success. */ bool avb_rsa_verify(const uint8_t* key, size_t key_num_bytes, const uint8_t* sig, size_t sig_num_bytes, const uint8_t* hash, size_t hash_num_bytes, const uint8_t* padding, size_t padding_num_bytes) { uint8_t* buf = NULL; Key* parsed_key = NULL; bool success = false; ... parsed_key = parse_key_data(key, key_num_bytes); ... buf = (uint8_t*)avb_malloc(sig_num_bytes); ... avb_memcpy(buf, sig, sig_num_bytes); modpowF4(parsed_key, buf); /* Check padding bytes. * * Even though there are probably no timing issues here, we use * avb_safe_memcmp() just to be on the safe side. */ if (avb_safe_memcmp(buf, padding, padding_num_bytes)) { avb_error("Padding check failed.\n"); goto out; } /* Check hash. */ if (avb_safe_memcmp(buf + padding_num_bytes, hash, hash_num_bytes)) { avb_error("Hash check failed.\n"); goto out; } success = true; out: if (parsed_key != NULL) { free_parsed_key(parsed_key); } if (buf != NULL) { avb_free(buf); } return success; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。