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