avb_slot_verify.c revision 0f7de9479afef18835cdbfe3da07811a7993504e
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include "avb_slot_verify.h"
26#include "avb_chain_partition_descriptor.h"
27#include "avb_footer.h"
28#include "avb_hash_descriptor.h"
29#include "avb_kernel_cmdline_descriptor.h"
30#include "avb_sha.h"
31#include "avb_util.h"
32#include "avb_vbmeta_image.h"
33#include "avb_version.h"
34
35/* Maximum allow length (in bytes) of a partition name, including
36 * ab_suffix.
37 */
38#define PART_NAME_MAX_SIZE 32
39
40/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
41#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
42
43/* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */
44#define MAX_NUMBER_OF_VBMETA_IMAGES 32
45
46/* Maximum size of a vbmeta image - 64 KiB. */
47#define VBMETA_MAX_SIZE (64 * 1024)
48
49/* Helper function to see if we should continue with verification in
50 * allow_verification_error=true mode if something goes wrong. See the
51 * comments for the avb_slot_verify() function for more information.
52 */
53static inline bool result_should_continue(AvbSlotVerifyResult result) {
54  switch (result) {
55    case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
56    case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
57    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
58    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
59      return false;
60
61    case AVB_SLOT_VERIFY_RESULT_OK:
62    case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
63    case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
64    case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
65      return true;
66  }
67
68  return false;
69}
70
71static AvbSlotVerifyResult load_and_verify_hash_partition(
72    AvbOps* ops,
73    const char* const* requested_partitions,
74    const char* ab_suffix,
75    bool allow_verification_error,
76    const AvbDescriptor* descriptor,
77    AvbSlotVerifyData* slot_data) {
78  AvbHashDescriptor hash_desc;
79  const uint8_t* desc_partition_name = NULL;
80  const uint8_t* desc_salt;
81  const uint8_t* desc_digest;
82  char part_name[PART_NAME_MAX_SIZE];
83  AvbSlotVerifyResult ret;
84  AvbIOResult io_ret;
85  uint8_t* image_buf = NULL;
86  size_t part_num_read;
87  uint8_t* digest;
88  size_t digest_len;
89  const char* found;
90
91  if (!avb_hash_descriptor_validate_and_byteswap(
92          (const AvbHashDescriptor*)descriptor, &hash_desc)) {
93    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
94    goto out;
95  }
96
97  desc_partition_name =
98      ((const uint8_t*)descriptor) + sizeof(AvbHashDescriptor);
99  desc_salt = desc_partition_name + hash_desc.partition_name_len;
100  desc_digest = desc_salt + hash_desc.salt_len;
101
102  if (!avb_validate_utf8(desc_partition_name, hash_desc.partition_name_len)) {
103    avb_error("Partition name is not valid UTF-8.\n");
104    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
105    goto out;
106  }
107
108  if (!avb_str_concat(part_name,
109                      sizeof part_name,
110                      (const char*)desc_partition_name,
111                      hash_desc.partition_name_len,
112                      ab_suffix,
113                      avb_strlen(ab_suffix))) {
114    avb_error("Partition name and suffix does not fit.\n");
115    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
116    goto out;
117  }
118
119  image_buf = avb_malloc(hash_desc.image_size);
120  if (image_buf == NULL) {
121    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
122    goto out;
123  }
124
125  io_ret = ops->read_from_partition(ops,
126                                    part_name,
127                                    0 /* offset */,
128                                    hash_desc.image_size,
129                                    image_buf,
130                                    &part_num_read);
131  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
132    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
133    goto out;
134  } else if (io_ret != AVB_IO_RESULT_OK) {
135    avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
136    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
137    goto out;
138  }
139  if (part_num_read != hash_desc.image_size) {
140    avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL);
141    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
142    goto out;
143  }
144
145  if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) {
146    AvbSHA256Ctx sha256_ctx;
147    avb_sha256_init(&sha256_ctx);
148    avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
149    avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
150    digest = avb_sha256_final(&sha256_ctx);
151    digest_len = AVB_SHA256_DIGEST_SIZE;
152  } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) {
153    AvbSHA512Ctx sha512_ctx;
154    avb_sha512_init(&sha512_ctx);
155    avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
156    avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
157    digest = avb_sha512_final(&sha512_ctx);
158    digest_len = AVB_SHA512_DIGEST_SIZE;
159  } else {
160    avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
161    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
162    goto out;
163  }
164
165  if (digest_len != hash_desc.digest_len) {
166    avb_errorv(
167        part_name, ": Digest in descriptor not of expected size.\n", NULL);
168    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
169    goto out;
170  }
171
172  if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) {
173    avb_errorv(part_name,
174               ": Hash of data does not match digest in descriptor.\n",
175               NULL);
176    ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
177    goto out;
178  }
179
180  ret = AVB_SLOT_VERIFY_RESULT_OK;
181
182out:
183
184  if (ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) {
185    /* If this is the requested partition, copy to slot_data. */
186    found = avb_strv_find_str(requested_partitions,
187                              (const char*)desc_partition_name,
188                              hash_desc.partition_name_len);
189    if (found != NULL) {
190      AvbPartitionData* loaded_partition;
191      if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) {
192        avb_errorv(part_name, ": Too many loaded partitions.\n", NULL);
193        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
194        goto fail;
195      }
196      loaded_partition =
197          &slot_data->loaded_partitions[slot_data->num_loaded_partitions++];
198      loaded_partition->partition_name = avb_strdup(found);
199      loaded_partition->data_size = hash_desc.image_size;
200      loaded_partition->data = image_buf;
201      image_buf = NULL;
202    }
203  }
204
205fail:
206  if (image_buf != NULL) {
207    avb_free(image_buf);
208  }
209  return ret;
210}
211
212static AvbSlotVerifyResult load_and_verify_vbmeta(
213    AvbOps* ops,
214    const char* const* requested_partitions,
215    const char* ab_suffix,
216    bool allow_verification_error,
217    AvbVBMetaImageFlags toplevel_vbmeta_flags,
218    int rollback_index_location,
219    const char* partition_name,
220    size_t partition_name_len,
221    const uint8_t* expected_public_key,
222    size_t expected_public_key_length,
223    AvbSlotVerifyData* slot_data,
224    AvbAlgorithmType* out_algorithm_type) {
225  char full_partition_name[PART_NAME_MAX_SIZE];
226  AvbSlotVerifyResult ret;
227  AvbIOResult io_ret;
228  size_t vbmeta_offset;
229  size_t vbmeta_size;
230  uint8_t* vbmeta_buf = NULL;
231  size_t vbmeta_num_read;
232  AvbVBMetaVerifyResult vbmeta_ret;
233  const uint8_t* pk_data;
234  size_t pk_len;
235  AvbVBMetaImageHeader vbmeta_header;
236  uint64_t stored_rollback_index;
237  const AvbDescriptor** descriptors = NULL;
238  size_t num_descriptors;
239  size_t n;
240  bool is_main_vbmeta;
241  bool is_vbmeta_partition;
242  AvbVBMetaData* vbmeta_image_data = NULL;
243
244  ret = AVB_SLOT_VERIFY_RESULT_OK;
245
246  avb_assert(slot_data != NULL);
247
248  /* Since we allow top-level vbmeta in 'boot', use
249   * rollback_index_location to determine whether we're the main
250   * vbmeta struct.
251   */
252  is_main_vbmeta = (rollback_index_location == 0);
253  is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
254
255  if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
256    avb_error("Partition name is not valid UTF-8.\n");
257    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
258    goto out;
259  }
260
261  /* Construct full partition name. */
262  if (!avb_str_concat(full_partition_name,
263                      sizeof full_partition_name,
264                      partition_name,
265                      partition_name_len,
266                      ab_suffix,
267                      avb_strlen(ab_suffix))) {
268    avb_error("Partition name and suffix does not fit.\n");
269    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
270    goto out;
271  }
272
273  avb_debugv("Loading vbmeta struct from partition '",
274             full_partition_name,
275             "'.\n",
276             NULL);
277
278  /* If we're loading from the main vbmeta partition, the vbmeta
279   * struct is in the beginning. Otherwise we have to locate it via a
280   * footer.
281   */
282  if (is_vbmeta_partition) {
283    vbmeta_offset = 0;
284    vbmeta_size = VBMETA_MAX_SIZE;
285  } else {
286    uint8_t footer_buf[AVB_FOOTER_SIZE];
287    size_t footer_num_read;
288    AvbFooter footer;
289
290    io_ret = ops->read_from_partition(ops,
291                                      full_partition_name,
292                                      -AVB_FOOTER_SIZE,
293                                      AVB_FOOTER_SIZE,
294                                      footer_buf,
295                                      &footer_num_read);
296    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
297      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
298      goto out;
299    } else if (io_ret != AVB_IO_RESULT_OK) {
300      avb_errorv(full_partition_name, ": Error loading footer.\n", NULL);
301      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
302      goto out;
303    }
304    avb_assert(footer_num_read == AVB_FOOTER_SIZE);
305
306    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf,
307                                          &footer)) {
308      avb_errorv(full_partition_name, ": Error validating footer.\n", NULL);
309      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
310      goto out;
311    }
312
313    /* Basic footer sanity check since the data is untrusted. */
314    if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
315      avb_errorv(
316          full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
317      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
318      goto out;
319    }
320
321    vbmeta_offset = footer.vbmeta_offset;
322    vbmeta_size = footer.vbmeta_size;
323  }
324
325  vbmeta_buf = avb_malloc(vbmeta_size);
326  if (vbmeta_buf == NULL) {
327    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
328    goto out;
329  }
330
331  io_ret = ops->read_from_partition(ops,
332                                    full_partition_name,
333                                    vbmeta_offset,
334                                    vbmeta_size,
335                                    vbmeta_buf,
336                                    &vbmeta_num_read);
337  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
338    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
339    goto out;
340  } else if (io_ret != AVB_IO_RESULT_OK) {
341    /* If we're looking for 'vbmeta' but there is no such partition,
342     * go try to get it from the boot partition instead.
343     */
344    if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
345        is_vbmeta_partition) {
346      avb_debugv(full_partition_name,
347                 ": No such partition. Trying 'boot' instead.\n",
348                 NULL);
349      ret = load_and_verify_vbmeta(ops,
350                                   requested_partitions,
351                                   ab_suffix,
352                                   allow_verification_error,
353                                   0 /* toplevel_vbmeta_flags */,
354                                   0 /* rollback_index_location */,
355                                   "boot",
356                                   avb_strlen("boot"),
357                                   NULL /* expected_public_key */,
358                                   0 /* expected_public_key_length */,
359                                   slot_data,
360                                   out_algorithm_type);
361      goto out;
362    } else {
363      avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
364      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
365      goto out;
366    }
367  }
368  avb_assert(vbmeta_num_read <= vbmeta_size);
369
370  /* Check if the image is properly signed and get the public key used
371   * to sign the image.
372   */
373  vbmeta_ret =
374      avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len);
375  switch (vbmeta_ret) {
376    case AVB_VBMETA_VERIFY_RESULT_OK:
377      avb_assert(pk_data != NULL && pk_len > 0);
378      break;
379
380    case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
381    case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
382    case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
383      ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
384      avb_errorv(full_partition_name,
385                 ": Error verifying vbmeta image: ",
386                 avb_vbmeta_verify_result_to_string(vbmeta_ret),
387                 "\n",
388                 NULL);
389      if (!allow_verification_error) {
390        goto out;
391      }
392      break;
393
394    case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
395      /* No way to continue this case. */
396      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
397      avb_errorv(full_partition_name,
398                 ": Error verifying vbmeta image: invalid vbmeta header\n",
399                 NULL);
400      goto out;
401
402    case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
403      /* No way to continue this case. */
404      ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION;
405      avb_errorv(full_partition_name,
406                 ": Error verifying vbmeta image: unsupported AVB version\n",
407                 NULL);
408      goto out;
409  }
410
411  /* Byteswap the header. */
412  avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf,
413                                             &vbmeta_header);
414
415  /* If we're the toplevel, assign flags so they'll be passed down. */
416  if (is_main_vbmeta) {
417    toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags;
418  } else {
419    if (vbmeta_header.flags != 0) {
420      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
421      avb_errorv(full_partition_name,
422                 ": chained vbmeta image has non-zero flags\n",
423                 NULL);
424      goto out;
425    }
426  }
427
428  /* Check if key used to make signature matches what is expected. */
429  if (pk_data != NULL) {
430    if (expected_public_key != NULL) {
431      avb_assert(!is_main_vbmeta);
432      if (expected_public_key_length != pk_len ||
433          avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) {
434        avb_errorv(full_partition_name,
435                   ": Public key used to sign data does not match key in chain "
436                   "partition descriptor.\n",
437                   NULL);
438        ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
439        if (!allow_verification_error) {
440          goto out;
441        }
442      }
443    } else {
444      bool key_is_trusted = false;
445      const uint8_t* pk_metadata = NULL;
446      size_t pk_metadata_len = 0;
447
448      if (vbmeta_header.public_key_metadata_size > 0) {
449        pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) +
450                      vbmeta_header.authentication_data_block_size +
451                      vbmeta_header.public_key_metadata_offset;
452        pk_metadata_len = vbmeta_header.public_key_metadata_size;
453      }
454
455      avb_assert(is_main_vbmeta);
456      io_ret = ops->validate_vbmeta_public_key(
457          ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted);
458      if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
459        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
460        goto out;
461      } else if (io_ret != AVB_IO_RESULT_OK) {
462        avb_errorv(full_partition_name,
463                   ": Error while checking public key used to sign data.\n",
464                   NULL);
465        ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
466        goto out;
467      }
468      if (!key_is_trusted) {
469        avb_errorv(full_partition_name,
470                   ": Public key used to sign data rejected.\n",
471                   NULL);
472        ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
473        if (!allow_verification_error) {
474          goto out;
475        }
476      }
477    }
478  }
479
480  /* Check rollback index. */
481  io_ret = ops->read_rollback_index(
482      ops, rollback_index_location, &stored_rollback_index);
483  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
484    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
485    goto out;
486  } else if (io_ret != AVB_IO_RESULT_OK) {
487    avb_errorv(full_partition_name,
488               ": Error getting rollback index for location.\n",
489               NULL);
490    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
491    goto out;
492  }
493  if (vbmeta_header.rollback_index < stored_rollback_index) {
494    avb_errorv(
495        full_partition_name,
496        ": Image rollback index is less than the stored rollback index.\n",
497        NULL);
498    ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX;
499    if (!allow_verification_error) {
500      goto out;
501    }
502  }
503
504  /* Copy vbmeta to vbmeta_images before recursing. */
505  if (is_main_vbmeta) {
506    avb_assert(slot_data->num_vbmeta_images == 0);
507  } else {
508    avb_assert(slot_data->num_vbmeta_images > 0);
509  }
510  if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) {
511    avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL);
512    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
513    goto out;
514  }
515  vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++];
516  vbmeta_image_data->partition_name = avb_strdup(partition_name);
517  vbmeta_image_data->vbmeta_data = vbmeta_buf;
518  /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long
519   * and this includes data past the end of the image. Pass the
520   * actual size of the vbmeta image. Also, no need to use
521   * avb_safe_add() since the header has already been verified.
522   */
523  vbmeta_image_data->vbmeta_size =
524      sizeof(AvbVBMetaImageHeader) +
525      vbmeta_header.authentication_data_block_size +
526      vbmeta_header.auxiliary_data_block_size;
527  vbmeta_image_data->verify_result = vbmeta_ret;
528
529  /* Now go through all descriptors and take the appropriate action:
530   *
531   * - hash descriptor: Load data from partition, calculate hash, and
532   *   checks that it matches what's in the hash descriptor.
533   *
534   * - hashtree descriptor: Do nothing since verification happens
535   *   on-the-fly from within the OS.
536   *
537   * - chained partition descriptor: Load the footer, load the vbmeta
538   *   image, verify vbmeta image (includes rollback checks, hash
539   *   checks, bail on chained partitions).
540   */
541  descriptors =
542      avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors);
543  for (n = 0; n < num_descriptors; n++) {
544    AvbDescriptor desc;
545
546    if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
547      avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL);
548      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
549      goto out;
550    }
551
552    switch (desc.tag) {
553      case AVB_DESCRIPTOR_TAG_HASH: {
554        AvbSlotVerifyResult sub_ret;
555        sub_ret = load_and_verify_hash_partition(ops,
556                                                 requested_partitions,
557                                                 ab_suffix,
558                                                 allow_verification_error,
559                                                 descriptors[n],
560                                                 slot_data);
561        if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
562          ret = sub_ret;
563          if (!allow_verification_error || !result_should_continue(ret)) {
564            goto out;
565          }
566        }
567      } break;
568
569      case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: {
570        AvbSlotVerifyResult sub_ret;
571        AvbChainPartitionDescriptor chain_desc;
572        const uint8_t* chain_partition_name;
573        const uint8_t* chain_public_key;
574
575        /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */
576        if (!is_main_vbmeta) {
577          avb_errorv(full_partition_name,
578                     ": Encountered chain descriptor not in main image.\n",
579                     NULL);
580          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
581          goto out;
582        }
583
584        if (!avb_chain_partition_descriptor_validate_and_byteswap(
585                (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) {
586          avb_errorv(full_partition_name,
587                     ": Chain partition descriptor is invalid.\n",
588                     NULL);
589          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
590          goto out;
591        }
592
593        if (chain_desc.rollback_index_location == 0) {
594          avb_errorv(full_partition_name,
595                     ": Chain partition has invalid "
596                     "rollback_index_location field.\n",
597                     NULL);
598          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
599          goto out;
600        }
601
602        chain_partition_name = ((const uint8_t*)descriptors[n]) +
603                               sizeof(AvbChainPartitionDescriptor);
604        chain_public_key = chain_partition_name + chain_desc.partition_name_len;
605
606        sub_ret = load_and_verify_vbmeta(ops,
607                                         requested_partitions,
608                                         ab_suffix,
609                                         allow_verification_error,
610                                         toplevel_vbmeta_flags,
611                                         chain_desc.rollback_index_location,
612                                         (const char*)chain_partition_name,
613                                         chain_desc.partition_name_len,
614                                         chain_public_key,
615                                         chain_desc.public_key_len,
616                                         slot_data,
617                                         NULL /* out_algorithm_type */);
618        if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
619          ret = sub_ret;
620          if (!result_should_continue(ret)) {
621            goto out;
622          }
623        }
624      } break;
625
626      case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: {
627        const uint8_t* kernel_cmdline;
628        AvbKernelCmdlineDescriptor kernel_cmdline_desc;
629        bool apply_cmdline;
630
631        if (!avb_kernel_cmdline_descriptor_validate_and_byteswap(
632                (AvbKernelCmdlineDescriptor*)descriptors[n],
633                &kernel_cmdline_desc)) {
634          avb_errorv(full_partition_name,
635                     ": Kernel cmdline descriptor is invalid.\n",
636                     NULL);
637          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
638          goto out;
639        }
640
641        kernel_cmdline = ((const uint8_t*)descriptors[n]) +
642                         sizeof(AvbKernelCmdlineDescriptor);
643
644        if (!avb_validate_utf8(kernel_cmdline,
645                               kernel_cmdline_desc.kernel_cmdline_length)) {
646          avb_errorv(full_partition_name,
647                     ": Kernel cmdline is not valid UTF-8.\n",
648                     NULL);
649          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
650          goto out;
651        }
652
653        /* Compare the flags for top-level VBMeta struct with flags in
654         * the command-line descriptor so command-line snippets only
655         * intended for a certain mode (dm-verity enabled/disabled)
656         * are skipped if applicable.
657         */
658        apply_cmdline = true;
659        if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
660          if (kernel_cmdline_desc.flags &
661              AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) {
662            apply_cmdline = false;
663          }
664        } else {
665          if (kernel_cmdline_desc.flags &
666              AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) {
667            apply_cmdline = false;
668          }
669        }
670
671        if (apply_cmdline) {
672          if (slot_data->cmdline == NULL) {
673            slot_data->cmdline =
674                avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1);
675            if (slot_data->cmdline == NULL) {
676              ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
677              goto out;
678            }
679            avb_memcpy(slot_data->cmdline,
680                       kernel_cmdline,
681                       kernel_cmdline_desc.kernel_cmdline_length);
682          } else {
683            /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */
684            size_t orig_size = avb_strlen(slot_data->cmdline);
685            size_t new_size =
686                orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1;
687            char* new_cmdline = avb_calloc(new_size);
688            if (new_cmdline == NULL) {
689              ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
690              goto out;
691            }
692            avb_memcpy(new_cmdline, slot_data->cmdline, orig_size);
693            new_cmdline[orig_size] = ' ';
694            avb_memcpy(new_cmdline + orig_size + 1,
695                       kernel_cmdline,
696                       kernel_cmdline_desc.kernel_cmdline_length);
697            avb_free(slot_data->cmdline);
698            slot_data->cmdline = new_cmdline;
699          }
700        }
701      } break;
702
703      /* Explicit fall-through */
704      case AVB_DESCRIPTOR_TAG_PROPERTY:
705      case AVB_DESCRIPTOR_TAG_HASHTREE:
706        /* Do nothing. */
707        break;
708    }
709  }
710
711  if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
712    avb_errorv(
713        full_partition_name, ": Invalid rollback_index_location.\n", NULL);
714    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
715    goto out;
716  }
717
718  slot_data->rollback_indexes[rollback_index_location] =
719      vbmeta_header.rollback_index;
720
721  if (out_algorithm_type != NULL) {
722    *out_algorithm_type = (AvbAlgorithmType)vbmeta_header.algorithm_type;
723  }
724
725out:
726  /* If |vbmeta_image_data| isn't NULL it means that it adopted
727   * |vbmeta_buf| so in that case don't free it here.
728   */
729  if (vbmeta_image_data == NULL) {
730    if (vbmeta_buf != NULL) {
731      avb_free(vbmeta_buf);
732    }
733  }
734  if (descriptors != NULL) {
735    avb_free(descriptors);
736  }
737  return ret;
738}
739
740#define NUM_GUIDS 3
741
742/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
743 * values. Returns NULL on OOM, otherwise the cmdline with values
744 * replaced.
745 */
746static char* sub_cmdline(AvbOps* ops,
747                         const char* cmdline,
748                         const char* ab_suffix,
749                         bool using_boot_for_vbmeta) {
750  const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
751  const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
752                                        "$(ANDROID_BOOT_PARTUUID)",
753                                        "$(ANDROID_VBMETA_PARTUUID)"};
754  char* ret = NULL;
755  AvbIOResult io_ret;
756
757  /* Special-case for when the top-level vbmeta struct is in the boot
758   * partition.
759   */
760  if (using_boot_for_vbmeta) {
761    part_name_str[2] = "boot";
762  }
763
764  /* Replace unique partition GUIDs */
765  for (size_t n = 0; n < NUM_GUIDS; n++) {
766    char part_name[PART_NAME_MAX_SIZE];
767    char guid_buf[37];
768
769    if (!avb_str_concat(part_name,
770                        sizeof part_name,
771                        part_name_str[n],
772                        avb_strlen(part_name_str[n]),
773                        ab_suffix,
774                        avb_strlen(ab_suffix))) {
775      avb_error("Partition name and suffix does not fit.\n");
776      goto fail;
777    }
778
779    io_ret = ops->get_unique_guid_for_partition(
780        ops, part_name, guid_buf, sizeof guid_buf);
781    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
782      return NULL;
783    } else if (io_ret != AVB_IO_RESULT_OK) {
784      avb_error("Error getting unique GUID for partition.\n");
785      goto fail;
786    }
787
788    if (ret == NULL) {
789      ret = avb_replace(cmdline, replace_str[n], guid_buf);
790    } else {
791      char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
792      avb_free(ret);
793      ret = new_ret;
794    }
795    if (ret == NULL) {
796      goto fail;
797    }
798  }
799
800  return ret;
801
802fail:
803  if (ret != NULL) {
804    avb_free(ret);
805  }
806  return NULL;
807}
808
809static int cmdline_append_option(AvbSlotVerifyData* slot_data,
810                                 const char* key,
811                                 const char* value) {
812  size_t offset, key_len, value_len;
813  char* new_cmdline;
814
815  key_len = avb_strlen(key);
816  value_len = avb_strlen(value);
817
818  offset = 0;
819  if (slot_data->cmdline != NULL) {
820    offset = avb_strlen(slot_data->cmdline);
821    if (offset > 0) {
822      offset += 1;
823    }
824  }
825
826  new_cmdline = avb_calloc(offset + key_len + value_len + 2);
827  if (new_cmdline == NULL) {
828    return 0;
829  }
830  if (offset > 0) {
831    avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
832    new_cmdline[offset - 1] = ' ';
833  }
834  avb_memcpy(new_cmdline + offset, key, key_len);
835  new_cmdline[offset + key_len] = '=';
836  avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
837  if (slot_data->cmdline != NULL) {
838    avb_free(slot_data->cmdline);
839  }
840  slot_data->cmdline = new_cmdline;
841
842  return 1;
843}
844
845#define AVB_MAX_DIGITS_UINT64 32
846
847/* Writes |value| to |digits| in base 10 followed by a NUL byte.
848 * Returns number of characters written excluding the NUL byte.
849 */
850static size_t uint64_to_base10(uint64_t value,
851                               char digits[AVB_MAX_DIGITS_UINT64]) {
852  char rev_digits[AVB_MAX_DIGITS_UINT64];
853  size_t n, num_digits;
854
855  for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
856    rev_digits[num_digits++] = (value % 10) + '0';
857    value /= 10;
858    if (value == 0) {
859      break;
860    }
861  }
862
863  for (n = 0; n < num_digits; n++) {
864    digits[n] = rev_digits[num_digits - 1 - n];
865  }
866  digits[n] = '\0';
867  return n;
868}
869
870static int cmdline_append_version(AvbSlotVerifyData* slot_data,
871                                  const char* key,
872                                  uint64_t major_version,
873                                  uint64_t minor_version) {
874  char major_digits[AVB_MAX_DIGITS_UINT64];
875  char minor_digits[AVB_MAX_DIGITS_UINT64];
876  char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
877  size_t num_major_digits, num_minor_digits;
878
879  num_major_digits = uint64_to_base10(major_version, major_digits);
880  num_minor_digits = uint64_to_base10(minor_version, minor_digits);
881  avb_memcpy(combined, major_digits, num_major_digits);
882  combined[num_major_digits] = '.';
883  avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
884  combined[num_major_digits + 1 + num_minor_digits] = '\0';
885
886  return cmdline_append_option(slot_data, key, combined);
887}
888
889static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
890                                        const char* key,
891                                        uint64_t value) {
892  char digits[AVB_MAX_DIGITS_UINT64];
893  uint64_to_base10(value, digits);
894  return cmdline_append_option(slot_data, key, digits);
895}
896
897static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
898                              const char* key,
899                              const uint8_t* data,
900                              size_t data_len) {
901  char hex_digits[17] = "0123456789abcdef";
902  char* hex_data;
903  int ret;
904  size_t n;
905
906  hex_data = avb_malloc(data_len * 2 + 1);
907  if (hex_data == NULL) {
908    return 0;
909  }
910
911  for (n = 0; n < data_len; n++) {
912    hex_data[n * 2] = hex_digits[data[n] >> 4];
913    hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
914  }
915  hex_data[n * 2] = '\0';
916
917  ret = cmdline_append_option(slot_data, key, hex_data);
918  avb_free(hex_data);
919  return ret;
920}
921
922AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
923                                    const char* const* requested_partitions,
924                                    const char* ab_suffix,
925                                    bool allow_verification_error,
926                                    AvbSlotVerifyData** out_data) {
927  AvbSlotVerifyResult ret;
928  AvbSlotVerifyData* slot_data = NULL;
929  AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
930  AvbIOResult io_ret;
931  bool using_boot_for_vbmeta = false;
932
933  if (out_data != NULL) {
934    *out_data = NULL;
935  }
936
937  slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
938  if (slot_data == NULL) {
939    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
940    goto fail;
941  }
942  slot_data->vbmeta_images =
943      avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES);
944  if (slot_data->vbmeta_images == NULL) {
945    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
946    goto fail;
947  }
948  slot_data->loaded_partitions =
949      avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS);
950  if (slot_data->loaded_partitions == NULL) {
951    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
952    goto fail;
953  }
954
955  ret = load_and_verify_vbmeta(ops,
956                               requested_partitions,
957                               ab_suffix,
958                               allow_verification_error,
959                               0 /* toplevel_vbmeta_flags */,
960                               0 /* rollback_index_location */,
961                               "vbmeta",
962                               avb_strlen("vbmeta"),
963                               NULL /* expected_public_key */,
964                               0 /* expected_public_key_length */,
965                               slot_data,
966                               &algorithm_type);
967  if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
968    goto fail;
969  }
970
971  if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) {
972    avb_assert(avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") ==
973               0);
974    using_boot_for_vbmeta = true;
975  }
976
977  /* If things check out, mangle the kernel command-line as needed. */
978  if (result_should_continue(ret)) {
979    /* Fill in |ab_suffix| field. */
980    slot_data->ab_suffix = avb_strdup(ab_suffix);
981    if (slot_data->ab_suffix == NULL) {
982      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
983      goto fail;
984    }
985
986    /* Add androidboot.vbmeta.device option. */
987    if (!cmdline_append_option(slot_data,
988                               "androidboot.vbmeta.device",
989                               "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
990      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
991      goto fail;
992    }
993
994    /* Add androidboot.vbmeta.avb_version option. */
995    if (!cmdline_append_version(slot_data,
996                                "androidboot.vbmeta.avb_version",
997                                AVB_VERSION_MAJOR,
998                                AVB_VERSION_MINOR)) {
999      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1000      goto fail;
1001    }
1002
1003    /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
1004    if (slot_data->cmdline != NULL) {
1005      char* new_cmdline;
1006      new_cmdline = sub_cmdline(
1007          ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta);
1008      if (new_cmdline == NULL) {
1009        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1010        goto fail;
1011      }
1012      avb_free(slot_data->cmdline);
1013      slot_data->cmdline = new_cmdline;
1014    }
1015
1016    /* Set androidboot.avb.device_state to "locked" or "unlocked". */
1017    bool is_device_unlocked;
1018    io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
1019    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
1020      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1021      goto fail;
1022    } else if (io_ret != AVB_IO_RESULT_OK) {
1023      avb_error("Error getting device state.\n");
1024      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
1025      goto fail;
1026    }
1027    if (!cmdline_append_option(slot_data,
1028                               "androidboot.vbmeta.device_state",
1029                               is_device_unlocked ? "unlocked" : "locked")) {
1030      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1031      goto fail;
1032    }
1033
1034    /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
1035     * function as is used to sign vbmeta.
1036     */
1037    switch (algorithm_type) {
1038      /* Explicit fallthrough. */
1039      case AVB_ALGORITHM_TYPE_NONE:
1040      case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
1041      case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
1042      case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
1043        AvbSHA256Ctx ctx;
1044        size_t n, total_size = 0;
1045        avb_sha256_init(&ctx);
1046        for (n = 0; n < slot_data->num_vbmeta_images; n++) {
1047          avb_sha256_update(&ctx,
1048                            slot_data->vbmeta_images[n].vbmeta_data,
1049                            slot_data->vbmeta_images[n].vbmeta_size);
1050          total_size += slot_data->vbmeta_images[n].vbmeta_size;
1051        }
1052        if (!cmdline_append_option(
1053                slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
1054            !cmdline_append_uint64_base10(
1055                slot_data, "androidboot.vbmeta.size", total_size) ||
1056            !cmdline_append_hex(slot_data,
1057                                "androidboot.vbmeta.digest",
1058                                avb_sha256_final(&ctx),
1059                                AVB_SHA256_DIGEST_SIZE)) {
1060          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1061          goto fail;
1062        }
1063      } break;
1064      /* Explicit fallthrough. */
1065      case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
1066      case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
1067      case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
1068        AvbSHA512Ctx ctx;
1069        size_t n, total_size = 0;
1070        avb_sha512_init(&ctx);
1071        for (n = 0; n < slot_data->num_vbmeta_images; n++) {
1072          avb_sha512_update(&ctx,
1073                            slot_data->vbmeta_images[n].vbmeta_data,
1074                            slot_data->vbmeta_images[n].vbmeta_size);
1075          total_size += slot_data->vbmeta_images[n].vbmeta_size;
1076        }
1077        if (!cmdline_append_option(
1078                slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
1079            !cmdline_append_uint64_base10(
1080                slot_data, "androidboot.vbmeta.size", total_size) ||
1081            !cmdline_append_hex(slot_data,
1082                                "androidboot.vbmeta.digest",
1083                                avb_sha512_final(&ctx),
1084                                AVB_SHA512_DIGEST_SIZE)) {
1085          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
1086          goto fail;
1087        }
1088      } break;
1089      case _AVB_ALGORITHM_NUM_TYPES:
1090        avb_assert_not_reached();
1091        break;
1092    }
1093
1094    if (out_data != NULL) {
1095      *out_data = slot_data;
1096    } else {
1097      avb_slot_verify_data_free(slot_data);
1098    }
1099  }
1100
1101  if (!allow_verification_error) {
1102    avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
1103  }
1104
1105  return ret;
1106
1107fail:
1108  if (slot_data != NULL) {
1109    avb_slot_verify_data_free(slot_data);
1110  }
1111  return ret;
1112}
1113
1114void avb_slot_verify_data_free(AvbSlotVerifyData* data) {
1115  if (data->ab_suffix != NULL) {
1116    avb_free(data->ab_suffix);
1117  }
1118  if (data->cmdline != NULL) {
1119    avb_free(data->cmdline);
1120  }
1121  if (data->vbmeta_images != NULL) {
1122    size_t n;
1123    for (n = 0; n < data->num_vbmeta_images; n++) {
1124      AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n];
1125      if (vbmeta_image->partition_name != NULL) {
1126        avb_free(vbmeta_image->partition_name);
1127      }
1128      if (vbmeta_image->vbmeta_data != NULL) {
1129        avb_free(vbmeta_image->vbmeta_data);
1130      }
1131    }
1132    avb_free(data->vbmeta_images);
1133  }
1134  if (data->loaded_partitions != NULL) {
1135    size_t n;
1136    for (n = 0; n < data->num_loaded_partitions; n++) {
1137      AvbPartitionData* loaded_partition = &data->loaded_partitions[n];
1138      if (loaded_partition->partition_name != NULL) {
1139        avb_free(loaded_partition->partition_name);
1140      }
1141      if (loaded_partition->data != NULL) {
1142        avb_free(loaded_partition->data);
1143      }
1144    }
1145    avb_free(data->loaded_partitions);
1146  }
1147  avb_free(data);
1148}
1149
1150const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
1151  const char* ret = NULL;
1152
1153  switch (result) {
1154    case AVB_SLOT_VERIFY_RESULT_OK:
1155      ret = "OK";
1156      break;
1157    case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
1158      ret = "ERROR_OOM";
1159      break;
1160    case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
1161      ret = "ERROR_IO";
1162      break;
1163    case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
1164      ret = "ERROR_VERIFICATION";
1165      break;
1166    case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
1167      ret = "ERROR_ROLLBACK_INDEX";
1168      break;
1169    case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
1170      ret = "ERROR_PUBLIC_KEY_REJECTED";
1171      break;
1172    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
1173      ret = "ERROR_INVALID_METADATA";
1174      break;
1175    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
1176      ret = "ERROR_UNSUPPORTED_VERSION";
1177      break;
1178      /* Do not add a 'default:' case here because of -Wswitch. */
1179  }
1180
1181  if (ret == NULL) {
1182    avb_error("Unknown AvbSlotVerifyResult value.\n");
1183    ret = "(unknown)";
1184  }
1185
1186  return ret;
1187}
1188