1*897a1d94STom Rini // SPDX-License-Identifier: MIT
2d8f9d2afSIgor Opaniuk /*
3d8f9d2afSIgor Opaniuk * Copyright (C) 2016 The Android Open Source Project
4d8f9d2afSIgor Opaniuk */
5d8f9d2afSIgor Opaniuk
6d8f9d2afSIgor Opaniuk #include "avb_vbmeta_image.h"
7d8f9d2afSIgor Opaniuk #include "avb_crypto.h"
8d8f9d2afSIgor Opaniuk #include "avb_rsa.h"
9d8f9d2afSIgor Opaniuk #include "avb_sha.h"
10d8f9d2afSIgor Opaniuk #include "avb_util.h"
11d8f9d2afSIgor Opaniuk #include "avb_version.h"
12d8f9d2afSIgor Opaniuk
avb_vbmeta_image_verify(const uint8_t * data,size_t length,const uint8_t ** out_public_key_data,size_t * out_public_key_length)13d8f9d2afSIgor Opaniuk AvbVBMetaVerifyResult avb_vbmeta_image_verify(
14d8f9d2afSIgor Opaniuk const uint8_t* data,
15d8f9d2afSIgor Opaniuk size_t length,
16d8f9d2afSIgor Opaniuk const uint8_t** out_public_key_data,
17d8f9d2afSIgor Opaniuk size_t* out_public_key_length) {
18d8f9d2afSIgor Opaniuk AvbVBMetaVerifyResult ret;
19d8f9d2afSIgor Opaniuk AvbVBMetaImageHeader h;
20d8f9d2afSIgor Opaniuk uint8_t* computed_hash;
21d8f9d2afSIgor Opaniuk const AvbAlgorithmData* algorithm;
22d8f9d2afSIgor Opaniuk AvbSHA256Ctx sha256_ctx;
23d8f9d2afSIgor Opaniuk AvbSHA512Ctx sha512_ctx;
24d8f9d2afSIgor Opaniuk const uint8_t* header_block;
25d8f9d2afSIgor Opaniuk const uint8_t* authentication_block;
26d8f9d2afSIgor Opaniuk const uint8_t* auxiliary_block;
27d8f9d2afSIgor Opaniuk int verification_result;
28d8f9d2afSIgor Opaniuk
29d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER;
30d8f9d2afSIgor Opaniuk
31d8f9d2afSIgor Opaniuk if (out_public_key_data != NULL) {
32d8f9d2afSIgor Opaniuk *out_public_key_data = NULL;
33d8f9d2afSIgor Opaniuk }
34d8f9d2afSIgor Opaniuk if (out_public_key_length != NULL) {
35d8f9d2afSIgor Opaniuk *out_public_key_length = 0;
36d8f9d2afSIgor Opaniuk }
37d8f9d2afSIgor Opaniuk
38d8f9d2afSIgor Opaniuk /* Ensure magic is correct. */
39d8f9d2afSIgor Opaniuk if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
40d8f9d2afSIgor Opaniuk avb_error("Magic is incorrect.\n");
41d8f9d2afSIgor Opaniuk goto out;
42d8f9d2afSIgor Opaniuk }
43d8f9d2afSIgor Opaniuk
44d8f9d2afSIgor Opaniuk /* Before we byteswap, ensure length is long enough. */
45d8f9d2afSIgor Opaniuk if (length < sizeof(AvbVBMetaImageHeader)) {
46d8f9d2afSIgor Opaniuk avb_error("Length is smaller than header.\n");
47d8f9d2afSIgor Opaniuk goto out;
48d8f9d2afSIgor Opaniuk }
49d8f9d2afSIgor Opaniuk avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
50d8f9d2afSIgor Opaniuk &h);
51d8f9d2afSIgor Opaniuk
52d8f9d2afSIgor Opaniuk /* Ensure we don't attempt to access any fields if we do not meet
53d8f9d2afSIgor Opaniuk * the specified minimum version of libavb.
54d8f9d2afSIgor Opaniuk */
55d8f9d2afSIgor Opaniuk if ((h.required_libavb_version_major != AVB_VERSION_MAJOR) ||
56d8f9d2afSIgor Opaniuk (h.required_libavb_version_minor > AVB_VERSION_MINOR)) {
57d8f9d2afSIgor Opaniuk avb_error("Mismatch between image version and libavb version.\n");
58d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION;
59d8f9d2afSIgor Opaniuk goto out;
60d8f9d2afSIgor Opaniuk }
61d8f9d2afSIgor Opaniuk
62d8f9d2afSIgor Opaniuk /* Ensure |release_string| ends with a NUL byte. */
63d8f9d2afSIgor Opaniuk if (h.release_string[AVB_RELEASE_STRING_SIZE - 1] != '\0') {
64d8f9d2afSIgor Opaniuk avb_error("Release string does not end with a NUL byte.\n");
65d8f9d2afSIgor Opaniuk goto out;
66d8f9d2afSIgor Opaniuk }
67d8f9d2afSIgor Opaniuk
68d8f9d2afSIgor Opaniuk /* Ensure inner block sizes are multiple of 64. */
69d8f9d2afSIgor Opaniuk if ((h.authentication_data_block_size & 0x3f) != 0 ||
70d8f9d2afSIgor Opaniuk (h.auxiliary_data_block_size & 0x3f) != 0) {
71d8f9d2afSIgor Opaniuk avb_error("Block size is not a multiple of 64.\n");
72d8f9d2afSIgor Opaniuk goto out;
73d8f9d2afSIgor Opaniuk }
74d8f9d2afSIgor Opaniuk
75d8f9d2afSIgor Opaniuk /* Ensure block sizes all add up to at most |length|. */
76d8f9d2afSIgor Opaniuk uint64_t block_total = sizeof(AvbVBMetaImageHeader);
77d8f9d2afSIgor Opaniuk if (!avb_safe_add_to(&block_total, h.authentication_data_block_size) ||
78d8f9d2afSIgor Opaniuk !avb_safe_add_to(&block_total, h.auxiliary_data_block_size)) {
79d8f9d2afSIgor Opaniuk avb_error("Overflow while computing size of boot image.\n");
80d8f9d2afSIgor Opaniuk goto out;
81d8f9d2afSIgor Opaniuk }
82d8f9d2afSIgor Opaniuk if (block_total > length) {
83d8f9d2afSIgor Opaniuk avb_error("Block sizes add up to more than given length.\n");
84d8f9d2afSIgor Opaniuk goto out;
85d8f9d2afSIgor Opaniuk }
86d8f9d2afSIgor Opaniuk
87d8f9d2afSIgor Opaniuk uintptr_t data_ptr = (uintptr_t)data;
88d8f9d2afSIgor Opaniuk /* Ensure passed in memory doesn't wrap. */
89d8f9d2afSIgor Opaniuk if (!avb_safe_add(NULL, (uint64_t)data_ptr, length)) {
90d8f9d2afSIgor Opaniuk avb_error("Boot image location and length mismatch.\n");
91d8f9d2afSIgor Opaniuk goto out;
92d8f9d2afSIgor Opaniuk }
93d8f9d2afSIgor Opaniuk
94d8f9d2afSIgor Opaniuk /* Ensure hash and signature are entirely in the Authentication data block. */
95d8f9d2afSIgor Opaniuk uint64_t hash_end;
96d8f9d2afSIgor Opaniuk if (!avb_safe_add(&hash_end, h.hash_offset, h.hash_size) ||
97d8f9d2afSIgor Opaniuk hash_end > h.authentication_data_block_size) {
98d8f9d2afSIgor Opaniuk avb_error("Hash is not entirely in its block.\n");
99d8f9d2afSIgor Opaniuk goto out;
100d8f9d2afSIgor Opaniuk }
101d8f9d2afSIgor Opaniuk uint64_t signature_end;
102d8f9d2afSIgor Opaniuk if (!avb_safe_add(&signature_end, h.signature_offset, h.signature_size) ||
103d8f9d2afSIgor Opaniuk signature_end > h.authentication_data_block_size) {
104d8f9d2afSIgor Opaniuk avb_error("Signature is not entirely in its block.\n");
105d8f9d2afSIgor Opaniuk goto out;
106d8f9d2afSIgor Opaniuk }
107d8f9d2afSIgor Opaniuk
108d8f9d2afSIgor Opaniuk /* Ensure public key is entirely in the Auxiliary data block. */
109d8f9d2afSIgor Opaniuk uint64_t pubkey_end;
110d8f9d2afSIgor Opaniuk if (!avb_safe_add(&pubkey_end, h.public_key_offset, h.public_key_size) ||
111d8f9d2afSIgor Opaniuk pubkey_end > h.auxiliary_data_block_size) {
112d8f9d2afSIgor Opaniuk avb_error("Public key is not entirely in its block.\n");
113d8f9d2afSIgor Opaniuk goto out;
114d8f9d2afSIgor Opaniuk }
115d8f9d2afSIgor Opaniuk
116d8f9d2afSIgor Opaniuk /* Ensure public key metadata (if set) is entirely in the Auxiliary
117d8f9d2afSIgor Opaniuk * data block. */
118d8f9d2afSIgor Opaniuk if (h.public_key_metadata_size > 0) {
119d8f9d2afSIgor Opaniuk uint64_t pubkey_md_end;
120d8f9d2afSIgor Opaniuk if (!avb_safe_add(&pubkey_md_end,
121d8f9d2afSIgor Opaniuk h.public_key_metadata_offset,
122d8f9d2afSIgor Opaniuk h.public_key_metadata_size) ||
123d8f9d2afSIgor Opaniuk pubkey_md_end > h.auxiliary_data_block_size) {
124d8f9d2afSIgor Opaniuk avb_error("Public key metadata is not entirely in its block.\n");
125d8f9d2afSIgor Opaniuk goto out;
126d8f9d2afSIgor Opaniuk }
127d8f9d2afSIgor Opaniuk }
128d8f9d2afSIgor Opaniuk
129d8f9d2afSIgor Opaniuk /* Bail early if there's no hash or signature. */
130d8f9d2afSIgor Opaniuk if (h.algorithm_type == AVB_ALGORITHM_TYPE_NONE) {
131d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED;
132d8f9d2afSIgor Opaniuk goto out;
133d8f9d2afSIgor Opaniuk }
134d8f9d2afSIgor Opaniuk
135d8f9d2afSIgor Opaniuk /* Ensure algorithm field is supported. */
136d8f9d2afSIgor Opaniuk algorithm = avb_get_algorithm_data(h.algorithm_type);
137d8f9d2afSIgor Opaniuk if (!algorithm) {
138d8f9d2afSIgor Opaniuk avb_error("Invalid or unknown algorithm.\n");
139d8f9d2afSIgor Opaniuk goto out;
140d8f9d2afSIgor Opaniuk }
141d8f9d2afSIgor Opaniuk
142d8f9d2afSIgor Opaniuk /* Bail if the embedded hash size doesn't match the chosen algorithm. */
143d8f9d2afSIgor Opaniuk if (h.hash_size != algorithm->hash_len) {
144d8f9d2afSIgor Opaniuk avb_error("Embedded hash has wrong size.\n");
145d8f9d2afSIgor Opaniuk goto out;
146d8f9d2afSIgor Opaniuk }
147d8f9d2afSIgor Opaniuk
148d8f9d2afSIgor Opaniuk /* No overflow checks needed from here-on after since all block
149d8f9d2afSIgor Opaniuk * sizes and offsets have been verified above.
150d8f9d2afSIgor Opaniuk */
151d8f9d2afSIgor Opaniuk
152d8f9d2afSIgor Opaniuk header_block = data;
153d8f9d2afSIgor Opaniuk authentication_block = header_block + sizeof(AvbVBMetaImageHeader);
154d8f9d2afSIgor Opaniuk auxiliary_block = authentication_block + h.authentication_data_block_size;
155d8f9d2afSIgor Opaniuk
156d8f9d2afSIgor Opaniuk switch (h.algorithm_type) {
157d8f9d2afSIgor Opaniuk /* Explicit fall-through: */
158d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
159d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
160d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA256_RSA8192:
161d8f9d2afSIgor Opaniuk avb_sha256_init(&sha256_ctx);
162d8f9d2afSIgor Opaniuk avb_sha256_update(
163d8f9d2afSIgor Opaniuk &sha256_ctx, header_block, sizeof(AvbVBMetaImageHeader));
164d8f9d2afSIgor Opaniuk avb_sha256_update(
165d8f9d2afSIgor Opaniuk &sha256_ctx, auxiliary_block, h.auxiliary_data_block_size);
166d8f9d2afSIgor Opaniuk computed_hash = avb_sha256_final(&sha256_ctx);
167d8f9d2afSIgor Opaniuk break;
168d8f9d2afSIgor Opaniuk /* Explicit fall-through: */
169d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
170d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
171d8f9d2afSIgor Opaniuk case AVB_ALGORITHM_TYPE_SHA512_RSA8192:
172d8f9d2afSIgor Opaniuk avb_sha512_init(&sha512_ctx);
173d8f9d2afSIgor Opaniuk avb_sha512_update(
174d8f9d2afSIgor Opaniuk &sha512_ctx, header_block, sizeof(AvbVBMetaImageHeader));
175d8f9d2afSIgor Opaniuk avb_sha512_update(
176d8f9d2afSIgor Opaniuk &sha512_ctx, auxiliary_block, h.auxiliary_data_block_size);
177d8f9d2afSIgor Opaniuk computed_hash = avb_sha512_final(&sha512_ctx);
178d8f9d2afSIgor Opaniuk break;
179d8f9d2afSIgor Opaniuk default:
180d8f9d2afSIgor Opaniuk avb_error("Unknown algorithm.\n");
181d8f9d2afSIgor Opaniuk goto out;
182d8f9d2afSIgor Opaniuk }
183d8f9d2afSIgor Opaniuk
184d8f9d2afSIgor Opaniuk if (avb_safe_memcmp(authentication_block + h.hash_offset,
185d8f9d2afSIgor Opaniuk computed_hash,
186d8f9d2afSIgor Opaniuk h.hash_size) != 0) {
187d8f9d2afSIgor Opaniuk avb_error("Hash does not match!\n");
188d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH;
189d8f9d2afSIgor Opaniuk goto out;
190d8f9d2afSIgor Opaniuk }
191d8f9d2afSIgor Opaniuk
192d8f9d2afSIgor Opaniuk verification_result =
193d8f9d2afSIgor Opaniuk avb_rsa_verify(auxiliary_block + h.public_key_offset,
194d8f9d2afSIgor Opaniuk h.public_key_size,
195d8f9d2afSIgor Opaniuk authentication_block + h.signature_offset,
196d8f9d2afSIgor Opaniuk h.signature_size,
197d8f9d2afSIgor Opaniuk authentication_block + h.hash_offset,
198d8f9d2afSIgor Opaniuk h.hash_size,
199d8f9d2afSIgor Opaniuk algorithm->padding,
200d8f9d2afSIgor Opaniuk algorithm->padding_len);
201d8f9d2afSIgor Opaniuk
202d8f9d2afSIgor Opaniuk if (verification_result == 0) {
203d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH;
204d8f9d2afSIgor Opaniuk goto out;
205d8f9d2afSIgor Opaniuk }
206d8f9d2afSIgor Opaniuk
207d8f9d2afSIgor Opaniuk if (h.public_key_size > 0) {
208d8f9d2afSIgor Opaniuk if (out_public_key_data != NULL) {
209d8f9d2afSIgor Opaniuk *out_public_key_data = auxiliary_block + h.public_key_offset;
210d8f9d2afSIgor Opaniuk }
211d8f9d2afSIgor Opaniuk if (out_public_key_length != NULL) {
212d8f9d2afSIgor Opaniuk *out_public_key_length = h.public_key_size;
213d8f9d2afSIgor Opaniuk }
214d8f9d2afSIgor Opaniuk }
215d8f9d2afSIgor Opaniuk
216d8f9d2afSIgor Opaniuk ret = AVB_VBMETA_VERIFY_RESULT_OK;
217d8f9d2afSIgor Opaniuk
218d8f9d2afSIgor Opaniuk out:
219d8f9d2afSIgor Opaniuk return ret;
220d8f9d2afSIgor Opaniuk }
221d8f9d2afSIgor Opaniuk
avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader * src,AvbVBMetaImageHeader * dest)222d8f9d2afSIgor Opaniuk void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
223d8f9d2afSIgor Opaniuk AvbVBMetaImageHeader* dest) {
224d8f9d2afSIgor Opaniuk avb_memcpy(dest, src, sizeof(AvbVBMetaImageHeader));
225d8f9d2afSIgor Opaniuk
226d8f9d2afSIgor Opaniuk dest->required_libavb_version_major =
227d8f9d2afSIgor Opaniuk avb_be32toh(dest->required_libavb_version_major);
228d8f9d2afSIgor Opaniuk dest->required_libavb_version_minor =
229d8f9d2afSIgor Opaniuk avb_be32toh(dest->required_libavb_version_minor);
230d8f9d2afSIgor Opaniuk
231d8f9d2afSIgor Opaniuk dest->authentication_data_block_size =
232d8f9d2afSIgor Opaniuk avb_be64toh(dest->authentication_data_block_size);
233d8f9d2afSIgor Opaniuk dest->auxiliary_data_block_size =
234d8f9d2afSIgor Opaniuk avb_be64toh(dest->auxiliary_data_block_size);
235d8f9d2afSIgor Opaniuk
236d8f9d2afSIgor Opaniuk dest->algorithm_type = avb_be32toh(dest->algorithm_type);
237d8f9d2afSIgor Opaniuk
238d8f9d2afSIgor Opaniuk dest->hash_offset = avb_be64toh(dest->hash_offset);
239d8f9d2afSIgor Opaniuk dest->hash_size = avb_be64toh(dest->hash_size);
240d8f9d2afSIgor Opaniuk
241d8f9d2afSIgor Opaniuk dest->signature_offset = avb_be64toh(dest->signature_offset);
242d8f9d2afSIgor Opaniuk dest->signature_size = avb_be64toh(dest->signature_size);
243d8f9d2afSIgor Opaniuk
244d8f9d2afSIgor Opaniuk dest->public_key_offset = avb_be64toh(dest->public_key_offset);
245d8f9d2afSIgor Opaniuk dest->public_key_size = avb_be64toh(dest->public_key_size);
246d8f9d2afSIgor Opaniuk
247d8f9d2afSIgor Opaniuk dest->public_key_metadata_offset =
248d8f9d2afSIgor Opaniuk avb_be64toh(dest->public_key_metadata_offset);
249d8f9d2afSIgor Opaniuk dest->public_key_metadata_size = avb_be64toh(dest->public_key_metadata_size);
250d8f9d2afSIgor Opaniuk
251d8f9d2afSIgor Opaniuk dest->descriptors_offset = avb_be64toh(dest->descriptors_offset);
252d8f9d2afSIgor Opaniuk dest->descriptors_size = avb_be64toh(dest->descriptors_size);
253d8f9d2afSIgor Opaniuk
254d8f9d2afSIgor Opaniuk dest->rollback_index = avb_be64toh(dest->rollback_index);
255d8f9d2afSIgor Opaniuk dest->flags = avb_be32toh(dest->flags);
256d8f9d2afSIgor Opaniuk }
257d8f9d2afSIgor Opaniuk
avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result)258d8f9d2afSIgor Opaniuk const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) {
259d8f9d2afSIgor Opaniuk const char* ret = NULL;
260d8f9d2afSIgor Opaniuk
261d8f9d2afSIgor Opaniuk switch (result) {
262d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_OK:
263d8f9d2afSIgor Opaniuk ret = "OK";
264d8f9d2afSIgor Opaniuk break;
265d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
266d8f9d2afSIgor Opaniuk ret = "OK_NOT_SIGNED";
267d8f9d2afSIgor Opaniuk break;
268d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
269d8f9d2afSIgor Opaniuk ret = "INVALID_VBMETA_HEADER";
270d8f9d2afSIgor Opaniuk break;
271d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
272d8f9d2afSIgor Opaniuk ret = "UNSUPPORTED_VERSION";
273d8f9d2afSIgor Opaniuk break;
274d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
275d8f9d2afSIgor Opaniuk ret = "HASH_MISMATCH";
276d8f9d2afSIgor Opaniuk break;
277d8f9d2afSIgor Opaniuk case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
278d8f9d2afSIgor Opaniuk ret = "SIGNATURE_MISMATCH";
279d8f9d2afSIgor Opaniuk break;
280d8f9d2afSIgor Opaniuk /* Do not add a 'default:' case here because of -Wswitch. */
281d8f9d2afSIgor Opaniuk }
282d8f9d2afSIgor Opaniuk
283d8f9d2afSIgor Opaniuk if (ret == NULL) {
284d8f9d2afSIgor Opaniuk avb_error("Unknown AvbVBMetaVerifyResult value.\n");
285d8f9d2afSIgor Opaniuk ret = "(unknown)";
286d8f9d2afSIgor Opaniuk }
287d8f9d2afSIgor Opaniuk
288d8f9d2afSIgor Opaniuk return ret;
289d8f9d2afSIgor Opaniuk }
290