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