1c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* 2c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * Copyright (C) 2015 The Android Open Source Project 3c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * 4c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * Licensed under the Apache License, Version 2.0 (the "License"); 5c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * you may not use this file except in compliance with the License. 6c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * You may obtain a copy of the License at 7c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * 8c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * http://www.apache.org/licenses/LICENSE-2.0 9c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * 10c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * Unless required by applicable law or agreed to in writing, software 11c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * distributed under the License is distributed on an "AS IS" BASIS, 12c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * See the License for the specific language governing permissions and 14c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen * limitations under the License. 15c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen */ 16c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 17c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen#include <ctype.h> 18c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen#include <stdlib.h> 1966dd09e8e2407082ce93bf0784de641298131912Elliott Hughes#include <android-base/strings.h> 20c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen#include "fec_private.h" 21c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 22c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* converts a hex nibble into an int */ 23c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic inline int hextobin(char c) 24c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 25c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (c >= '0' && c <= '9') { 26c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return c - '0'; 27c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } else if (c >= 'a' && c <= 'f') { 28c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return c - 'a' + 10; 29c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } else { 30c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = EINVAL; 31c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 32c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 33c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 34c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 35c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* converts a hex string `src' of `size' characters to binary and copies the 36c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen the result into `dst' */ 37c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic int parse_hex(uint8_t *dst, uint32_t size, const char *src) 38c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 39c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen int l, h; 40c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 41c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(dst); 42c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(src); 43c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(2 * size == strlen(src)); 44c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 45c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen while (size) { 46c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen h = hextobin(tolower(*src++)); 47c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen l = hextobin(tolower(*src++)); 48c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 49c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(l >= 0); 50c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(h >= 0); 51c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 52c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen *dst++ = (h << 4) | l; 53c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen --size; 54c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 55c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 56c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 57c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 58c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 59c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* parses a 64-bit unsigned integer from string `src' into `dst' and if 60c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen `maxval' is >0, checks that `dst' <= `maxval' */ 61c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic int parse_uint64(const char *src, uint64_t maxval, uint64_t *dst) 62c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 63c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen char *end; 64c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen unsigned long long int value; 65c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 66c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(src); 67c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(dst); 68c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 69c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = 0; 70c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen value = strtoull(src, &end, 0); 71c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 72c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (*src == '\0' || *end != '\0' || 73c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen (errno == ERANGE && value == ULLONG_MAX)) { 74c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = EINVAL; 75c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 76c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 77c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 78c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (maxval && value > maxval) { 79c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = EINVAL; 80c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 81c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 82c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 83c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen *dst = (uint64_t)value; 84c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 85c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 86c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 87c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* computes the size of verity hash tree for `file_size' bytes and returns the 88c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen number of hash tree levels in `verity_levels,' and the number of hashes per 89c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen level in `level_hashes', if the parameters are non-NULL */ 90c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenuint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels, 91c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t *level_hashes) 92c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 93c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* we assume a known metadata size, 4 KiB block size, and SHA-256 to avoid 94c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen relying on disk content */ 95c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 96c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t level = 0; 97c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint64_t total = 0; 98c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint64_t hashes = file_size / FEC_BLOCKSIZE; 99c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 100c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen do { 101c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (level_hashes) { 102c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen level_hashes[level] = hashes; 103c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 104c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 105c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hashes = fec_div_round_up(hashes * SHA256_DIGEST_LENGTH, FEC_BLOCKSIZE); 106c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen total += hashes; 107c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 108c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen ++level; 109c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } while (hashes > 1); 110c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 111c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (verity_levels) { 112c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen *verity_levels = level; 113c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 114c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 115c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return total * FEC_BLOCKSIZE; 116c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 117c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 118c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* computes a SHA-256 salted with `f->verity.salt' from a FEC_BLOCKSIZE byte 119c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen buffer `block', and copies the hash to `hash' */ 120c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic inline int verity_hash(fec_handle *f, const uint8_t *block, 121c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t *hash) 122c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 123c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_CTX ctx; 124c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_Init(&ctx); 125c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 126c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 127c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f->verity.salt); 128c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_Update(&ctx, f->verity.salt, f->verity.salt_size); 129c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 130c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(block); 131c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_Update(&ctx, block, FEC_BLOCKSIZE); 132c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 133c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(hash); 134c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_Final(hash, &ctx); 135c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 136c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 137c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 138c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* computes a verity hash for FEC_BLOCKSIZE bytes from buffer `block' and 139dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen compares it to the expected value in `expected' */ 140dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanenbool verity_check_block(fec_handle *f, const uint8_t *expected, 141c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen const uint8_t *block) 142c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 143c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 144dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen check(block); 145c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 146c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t hash[SHA256_DIGEST_LENGTH]; 147c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 148c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (unlikely(verity_hash(f, block, hash) == -1)) { 149c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to hash"); 150c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return false; 151c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 152c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 153c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(expected); 154dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen return !memcmp(expected, hash, SHA256_DIGEST_LENGTH); 155c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 156c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 157c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* reads a verity hash and the corresponding data block using error correction, 158c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if available */ 159c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic bool ecc_read_hashes(fec_handle *f, uint64_t hash_offset, 160c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t *hash, uint64_t data_offset, uint8_t *data) 161c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 162c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 163c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 164c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (hash && fec_pread(f, hash, SHA256_DIGEST_LENGTH, hash_offset) != 165c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen SHA256_DIGEST_LENGTH) { 166c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to read hash tree: offset %" PRIu64 ": %s", hash_offset, 167c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen strerror(errno)); 168c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return false; 169c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 170c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 171c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(data); 172c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 173c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (fec_pread(f, data, FEC_BLOCKSIZE, data_offset) != FEC_BLOCKSIZE) { 174c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to read hash tree: data_offset %" PRIu64 ": %s", 175c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset, strerror(errno)); 176c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return false; 177c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 178c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 179c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return true; 180c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 181c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 182c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* reads the verity hash tree, validates it against the root hash in `root', 183c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen corrects errors if necessary, and copies valid data blocks for later use 184c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen to `f->verity.hash' */ 185c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic int verify_tree(fec_handle *f, const uint8_t *root) 186c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 187c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t data[FEC_BLOCKSIZE]; 188c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t hash[SHA256_DIGEST_LENGTH]; 189c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 190c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 191c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(root); 192c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 193c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_info *v = &f->verity; 194c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t levels = 0; 195c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 196c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* calculate the size and the number of levels in the hash tree */ 197c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash_size = 198c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_get_size(v->data_blocks * FEC_BLOCKSIZE, &levels, NULL); 199c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 200c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_start < UINT64_MAX - v->hash_size); 201c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_start + v->hash_size <= f->data_size); 202c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 203c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint64_t hash_offset = v->hash_start; 204c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint64_t data_offset = hash_offset + FEC_BLOCKSIZE; 205c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 206c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash_data_offset = data_offset; 207c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 208c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* validate the root hash */ 209c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!raw_pread(f, data, FEC_BLOCKSIZE, hash_offset) || 210dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen !verity_check_block(f, root, data)) { 211c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* try to correct */ 212c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!ecc_read_hashes(f, 0, NULL, hash_offset, data) || 213dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen !verity_check_block(f, root, data)) { 214c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("root hash invalid"); 215c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 216c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } else if (f->mode & O_RDWR && 217c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen !raw_pwrite(f, data, FEC_BLOCKSIZE, hash_offset)) { 218c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to rewrite the root block: %s", strerror(errno)); 219c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 220c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 221c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 222c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 223c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("root hash valid"); 224c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 225c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* calculate the number of hashes on each level */ 226c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t hashes[levels]; 227c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 228c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_get_size(v->data_blocks * FEC_BLOCKSIZE, NULL, hashes); 229c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 230c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* calculate the size and offset for the data hashes */ 231c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen for (uint32_t i = 1; i < levels; ++i) { 232c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t blocks = hashes[levels - i]; 233c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("%u hash blocks on level %u", blocks, levels - i); 234c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 235c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash_data_offset = data_offset; 236c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash_data_blocks = blocks; 237c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 238c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset += blocks * FEC_BLOCKSIZE; 239c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 240c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 241c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_blocks); 242c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_blocks <= v->hash_size / FEC_BLOCKSIZE); 243c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 244c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_offset); 245c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_offset <= 246c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen UINT64_MAX - (v->hash_data_blocks * FEC_BLOCKSIZE)); 247c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_offset < f->data_size); 248c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_data_offset + v->hash_data_blocks * FEC_BLOCKSIZE <= 249c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen f->data_size); 250c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 251c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* copy data hashes to memory in case they are corrupted, so we don't 252c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen have to correct them every time they are needed */ 253c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen std::unique_ptr<uint8_t[]> data_hashes( 254c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen new (std::nothrow) uint8_t[f->verity.hash_data_blocks * FEC_BLOCKSIZE]); 255c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 256c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!data_hashes) { 257c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = ENOMEM; 258c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 259c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 260c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 261c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* validate the rest of the hash tree */ 262c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset = hash_offset + FEC_BLOCKSIZE; 263c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 264c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen for (uint32_t i = 1; i < levels; ++i) { 265c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint32_t blocks = hashes[levels - i]; 266c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 267c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen for (uint32_t j = 0; j < blocks; ++j) { 268c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* ecc reads are very I/O intensive, so read raw hash tree and do 269c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error correcting only if it doesn't validate */ 270c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!raw_pread(f, hash, SHA256_DIGEST_LENGTH, 271c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hash_offset + j * SHA256_DIGEST_LENGTH) || 272c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen !raw_pread(f, data, FEC_BLOCKSIZE, 273c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset + j * FEC_BLOCKSIZE)) { 274c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to read hashes: %s", strerror(errno)); 275c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 276c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 277c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 278dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen if (!verity_check_block(f, hash, data)) { 279c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* try to correct */ 280c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!ecc_read_hashes(f, 281c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hash_offset + j * SHA256_DIGEST_LENGTH, hash, 282c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset + j * FEC_BLOCKSIZE, data) || 283dadd5e33ac00df9a57114487f8441a59fd08bd89Sami Tolvanen !verity_check_block(f, hash, data)) { 284c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("invalid hash tree: hash_offset %" PRIu64 ", " 285c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen "data_offset %" PRIu64 ", block %u", 286c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hash_offset, data_offset, j); 287c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 288c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 289c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 290c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* update the corrected blocks to the file if we are in r/w 291c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen mode */ 292c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (f->mode & O_RDWR) { 293c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!raw_pwrite(f, hash, SHA256_DIGEST_LENGTH, 294c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hash_offset + j * SHA256_DIGEST_LENGTH) || 295c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen !raw_pwrite(f, data, FEC_BLOCKSIZE, 296c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset + j * FEC_BLOCKSIZE)) { 297c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to write hashes: %s", strerror(errno)); 298c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 299c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 300c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 301c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 302c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 303c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (blocks == v->hash_data_blocks) { 304c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen memcpy(data_hashes.get() + j * FEC_BLOCKSIZE, data, 305c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen FEC_BLOCKSIZE); 306c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 307c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 308c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 309c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen hash_offset = data_offset; 310c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen data_offset += blocks * FEC_BLOCKSIZE; 311c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 312c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 313c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("valid"); 314c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 315c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash = data_hashes.release(); 316c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 317c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 318c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 319c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* reads, corrects and parses the verity table, validates parameters, and if 320c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen `f->flags' does not have `FEC_VERITY_DISABLE' set, calls `verify_tree' to 321c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen load and validate the hash tree */ 322c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic int parse_table(fec_handle *f, uint64_t offset, uint32_t size) 323c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 324c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 325c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(size >= VERITY_MIN_TABLE_SIZE); 326c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(size <= VERITY_MAX_TABLE_SIZE); 327c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 328c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("offset = %" PRIu64 ", size = %u", offset, size); 329c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 330c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_info *v = &f->verity; 331c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen std::unique_ptr<char[]> table(new (std::nothrow) char[size + 1]); 332c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 333c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!table) { 334c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = ENOMEM; 335c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 336c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 337c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 338c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (fec_pread(f, table.get(), size, offset) != (ssize_t)size) { 339c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to read verity table: %s", strerror(errno)); 340c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 341c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 342c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 343c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen table[size] = '\0'; 344c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("verity table: '%s'", table.get()); 345c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 346c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen int i = 0; 347c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen std::unique_ptr<uint8_t[]> salt; 348c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t root[SHA256_DIGEST_LENGTH]; 349c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 350c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen auto tokens = android::base::Split(table.get(), " "); 351c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 352c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen for (const auto token : tokens) { 353c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen switch (i++) { 354c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 0: /* version */ 355c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (token != stringify(VERITY_TABLE_VERSION)) { 356c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("unsupported verity table version: %s", token.c_str()); 357c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 358c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 359c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 360c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 3: /* data_block_size */ 361c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 4: /* hash_block_size */ 362c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* assume 4 KiB block sizes for everything */ 363c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (token != stringify(FEC_BLOCKSIZE)) { 364c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("unsupported verity block size: %s", token.c_str()); 365c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 366c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 367c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 368c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 5: /* num_data_blocks */ 369c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE, 370c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen &v->data_blocks) == -1) { 371c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("invalid number of verity data blocks: %s", 372c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen token.c_str()); 373c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 374c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 375c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 376c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 6: /* hash_start_block */ 377c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE, 378c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen &v->hash_start) == -1) { 379c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("invalid verity hash start block: %s", token.c_str()); 380c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 381c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 382c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 383c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->hash_start *= FEC_BLOCKSIZE; 384c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 385c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 7: /* algorithm */ 386c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (token != "sha256") { 387c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("unsupported verity hash algorithm: %s", token.c_str()); 388c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 389c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 390c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 391c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 8: /* digest */ 392c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (parse_hex(root, sizeof(root), token.c_str()) == -1) { 393c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("invalid verity root hash: %s", token.c_str()); 394c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 395c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 396c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 397c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen case 9: /* salt */ 398c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->salt_size = token.size(); 399c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->salt_size % 2 == 0); 400c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->salt_size /= 2; 401c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 402c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen salt.reset(new (std::nothrow) uint8_t[v->salt_size]); 403c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 404c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!salt) { 405c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = ENOMEM; 406c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 407c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 408c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 409c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (parse_hex(salt.get(), v->salt_size, token.c_str()) == -1) { 410c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("invalid verity salt: %s", token.c_str()); 411c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 412c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 413c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 414c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen default: 415c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen break; 416c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 417c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 418c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 419c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (i < VERITY_TABLE_ARGS) { 420c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("not enough arguments in verity table: %d; expected at least " 421c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen stringify(VERITY_TABLE_ARGS), i); 422c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 423c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 424c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 425c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash_start < f->data_size); 426c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 427c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (v->metadata_start < v->hash_start) { 428c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->data_blocks == v->metadata_start / FEC_BLOCKSIZE); 429c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } else { 430c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->data_blocks == v->hash_start / FEC_BLOCKSIZE); 431c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 432c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 433c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->salt = salt.release(); 434c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->table = table.release(); 435c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 436c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!(f->flags & FEC_VERITY_DISABLE)) { 437c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (verify_tree(f, root) == -1) { 438c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 439c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 440c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 441c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->hash); 442c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 443c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint8_t zero_block[FEC_BLOCKSIZE]; 444c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen memset(zero_block, 0, FEC_BLOCKSIZE); 445c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 446c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (verity_hash(f, zero_block, v->zero_hash) == -1) { 447c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to hash"); 448c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 449c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 450c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 451c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 452c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 453c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 454c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 455c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* rewrites verity metadata block using error corrected data in `f->verity' */ 456c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenstatic int rewrite_metadata(fec_handle *f, uint64_t offset) 457c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 458c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 459c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f->data_size > VERITY_METADATA_SIZE); 460c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(offset <= f->data_size - VERITY_METADATA_SIZE); 461c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 462c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen std::unique_ptr<uint8_t[]> metadata( 463c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen new (std::nothrow) uint8_t[VERITY_METADATA_SIZE]); 464c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 465c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (!metadata) { 466c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen errno = ENOMEM; 467c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 468c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 469c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 470c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen memset(metadata.get(), 0, VERITY_METADATA_SIZE); 471c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 472c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_info *v = &f->verity; 473c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen memcpy(metadata.get(), &v->header, sizeof(v->header)); 474c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 475c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(v->table); 476c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen size_t len = strlen(v->table); 477c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 478c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(sizeof(v->header) + len <= VERITY_METADATA_SIZE); 479c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen memcpy(metadata.get() + sizeof(v->header), v->table, len); 480c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 481c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return raw_pwrite(f, metadata.get(), VERITY_METADATA_SIZE, offset); 482c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 483c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 48465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanenstatic int validate_header(const fec_handle *f, const verity_header *header, 48565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen uint64_t offset) 48665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen{ 48765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen check(f); 48865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen check(header); 48965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 49065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (header->magic != VERITY_MAGIC && 49165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen header->magic != VERITY_MAGIC_DISABLE) { 49265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; 49365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 49465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 49565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (header->version != VERITY_VERSION) { 49665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen error("unsupported verity version %u", header->version); 49765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; 49865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 49965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 50065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (header->length < VERITY_MIN_TABLE_SIZE || 50165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen header->length > VERITY_MAX_TABLE_SIZE) { 50265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen error("invalid verity table size: %u; expected [" 50365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen stringify(VERITY_MIN_TABLE_SIZE) ", " 50465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen stringify(VERITY_MAX_TABLE_SIZE) ")", header->length); 50565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; 50665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 50765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 50865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen /* signature is skipped, because for our purposes it won't matter from 50965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen where the data originates; the caller of the library is responsible 51065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen for signature verification */ 51165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 51265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (offset > UINT64_MAX - header->length) { 51365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen error("invalid verity table length: %u", header->length); 51465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; 51565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } else if (offset + header->length >= f->data_size) { 51665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen error("invalid verity table length: %u", header->length); 51765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; 51865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 51965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 52065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return 0; 52165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen} 52265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 523c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen/* attempts to read verity metadata from `f->fd' position `offset'; if in r/w 524c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen mode, rewrites the metadata if it had errors */ 525c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanenint verity_parse_header(fec_handle *f, uint64_t offset) 526c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen{ 527c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f); 528c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen check(f->data_size > VERITY_METADATA_SIZE); 529c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 530c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (offset > f->data_size - VERITY_METADATA_SIZE) { 531c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen debug("failed to read verity header: offset %" PRIu64 " is too far", 532c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen offset); 533c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 534c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 535c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 536c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen verity_info *v = &f->verity; 537c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen uint64_t errors = f->errors; 538c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 53965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (!raw_pread(f, &v->header, sizeof(v->header), offset)) { 540c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen error("failed to read verity header: %s", strerror(errno)); 541c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 542c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 543c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 544c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* use raw data to check for the alternative magic, because it will 545c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen be error corrected to VERITY_MAGIC otherwise */ 54665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (v->header.magic == VERITY_MAGIC_DISABLE) { 547c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* this value is not used by us, but can be used by a caller to 548c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen decide whether dm-verity should be enabled */ 549c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen v->disabled = true; 550c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 551c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 55265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (fec_pread(f, &v->ecc_header, sizeof(v->ecc_header), offset) != 55365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen sizeof(v->ecc_header)) { 55465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen warn("failed to read verity header: %s", strerror(errno)); 555c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 556c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 557c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 55865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (validate_header(f, &v->header, offset)) { 55965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen /* raw verity header is invalid; this could be due to corruption, or 56065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen due to missing verity metadata */ 561c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 56265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (validate_header(f, &v->ecc_header, offset)) { 56365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen return -1; /* either way, we cannot recover */ 56465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 565c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 56665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen /* report mismatching fields */ 56765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (!v->disabled && v->header.magic != v->ecc_header.magic) { 56865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen warn("corrected verity header magic"); 56965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen v->header.magic = v->ecc_header.magic; 57065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 571c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 57265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (v->header.version != v->ecc_header.version) { 57365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen warn("corrected verity header version"); 57465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen v->header.version = v->ecc_header.version; 57565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 57665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 57765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (v->header.length != v->ecc_header.length) { 57865cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen warn("corrected verity header length"); 57965cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen v->header.length = v->ecc_header.length; 58065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 58165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 58265cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen if (memcmp(v->header.signature, v->ecc_header.signature, 58365cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen sizeof(v->header.signature))) { 58465cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen warn("corrected verity header signature"); 58565cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen /* we have no way of knowing which signature is correct, if either 58665cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen of them is */ 58765cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen } 588c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 589c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 59065cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen v->metadata_start = offset; 59165cbaeb020b209f3d75d594ebbe49a609dd3c7e7Sami Tolvanen 592c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (parse_table(f, offset + sizeof(v->header), v->header.length) == -1) { 593c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return -1; 594c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 595c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 596c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen /* if we corrected something while parsing metadata and we are in r/w 597c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen mode, rewrite the corrected metadata */ 598c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (f->mode & O_RDWR && f->errors > errors && 599c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen rewrite_metadata(f, offset) < 0) { 600c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen warn("failed to rewrite verity metadata: %s", strerror(errno)); 601c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 602c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 603c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen if (v->metadata_start < v->hash_start) { 604c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen f->data_size = v->metadata_start; 605c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } else { 606c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen f->data_size = v->hash_start; 607c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen } 608c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen 609c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen return 0; 610c54a33db7505976a3530aa76ebd5602f12923c4dSami Tolvanen} 61183cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 61283cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanenint fec_verity_set_status(struct fec_handle *f, bool enabled) 61383cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen{ 61483cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen check(f); 61583cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 61683cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen if (!(f->mode & O_RDWR)) { 61783cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen error("cannot update verity magic: read-only handle"); 61883cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen errno = EBADF; 61983cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen return -1; 62083cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen } 62183cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 62283cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen verity_info *v = &f->verity; 62383cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 62483cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen if (!v->metadata_start) { 62583cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen error("cannot update verity magic: no metadata found"); 62683cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen errno = EINVAL; 62783cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen return -1; 62883cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen } 62983cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 63083cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen if (v->disabled == !enabled) { 63183cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen return 0; /* nothing to do */ 63283cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen } 63383cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 63483cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen uint32_t magic = enabled ? VERITY_MAGIC : VERITY_MAGIC_DISABLE; 63583cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 63683cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen if (!raw_pwrite(f, &magic, sizeof(magic), v->metadata_start)) { 63783cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen error("failed to update verity magic to %08x: %s", magic, 63883cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen strerror(errno)); 63983cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen return -1; 64083cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen } 64183cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 64283cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen warn("updated verity magic to %08x (%s)", magic, 64383cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen enabled ? "enabled" : "disabled"); 64483cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen v->disabled = !enabled; 64583cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen 64683cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen return 0; 64783cda15b15269721aa4c5680af2fc33ffd30dfa3Sami Tolvanen} 648