xref: /aosp_15_r20/system/extras/libfec/fec_verity.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker #include <ctype.h>
18*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
19*288bf522SAndroid Build Coastguard Worker 
20*288bf522SAndroid Build Coastguard Worker #include <algorithm>
21*288bf522SAndroid Build Coastguard Worker #include <string>
22*288bf522SAndroid Build Coastguard Worker #include <vector>
23*288bf522SAndroid Build Coastguard Worker 
24*288bf522SAndroid Build Coastguard Worker #include <android-base/strings.h>
25*288bf522SAndroid Build Coastguard Worker #include <openssl/evp.h>
26*288bf522SAndroid Build Coastguard Worker 
27*288bf522SAndroid Build Coastguard Worker #include "fec_private.h"
28*288bf522SAndroid Build Coastguard Worker 
29*288bf522SAndroid Build Coastguard Worker /* converts a hex nibble into an int */
hextobin(char c)30*288bf522SAndroid Build Coastguard Worker static inline int hextobin(char c)
31*288bf522SAndroid Build Coastguard Worker {
32*288bf522SAndroid Build Coastguard Worker     if (c >= '0' && c <= '9') {
33*288bf522SAndroid Build Coastguard Worker         return c - '0';
34*288bf522SAndroid Build Coastguard Worker     } else if (c >= 'a' && c <= 'f') {
35*288bf522SAndroid Build Coastguard Worker         return c - 'a' + 10;
36*288bf522SAndroid Build Coastguard Worker     } else {
37*288bf522SAndroid Build Coastguard Worker         errno = EINVAL;
38*288bf522SAndroid Build Coastguard Worker         return -1;
39*288bf522SAndroid Build Coastguard Worker     }
40*288bf522SAndroid Build Coastguard Worker }
41*288bf522SAndroid Build Coastguard Worker 
42*288bf522SAndroid Build Coastguard Worker /* converts a hex string `src' of `size' characters to binary and copies the
43*288bf522SAndroid Build Coastguard Worker    the result into `dst' */
parse_hex(uint8_t * dst,uint32_t size,const char * src)44*288bf522SAndroid Build Coastguard Worker static int parse_hex(uint8_t *dst, uint32_t size, const char *src)
45*288bf522SAndroid Build Coastguard Worker {
46*288bf522SAndroid Build Coastguard Worker     int l, h;
47*288bf522SAndroid Build Coastguard Worker 
48*288bf522SAndroid Build Coastguard Worker     check(dst);
49*288bf522SAndroid Build Coastguard Worker     check(src);
50*288bf522SAndroid Build Coastguard Worker     check(2 * size == strlen(src));
51*288bf522SAndroid Build Coastguard Worker 
52*288bf522SAndroid Build Coastguard Worker     while (size) {
53*288bf522SAndroid Build Coastguard Worker         h = hextobin(tolower(*src++));
54*288bf522SAndroid Build Coastguard Worker         l = hextobin(tolower(*src++));
55*288bf522SAndroid Build Coastguard Worker 
56*288bf522SAndroid Build Coastguard Worker         check(l >= 0);
57*288bf522SAndroid Build Coastguard Worker         check(h >= 0);
58*288bf522SAndroid Build Coastguard Worker 
59*288bf522SAndroid Build Coastguard Worker         *dst++ = (h << 4) | l;
60*288bf522SAndroid Build Coastguard Worker         --size;
61*288bf522SAndroid Build Coastguard Worker     }
62*288bf522SAndroid Build Coastguard Worker 
63*288bf522SAndroid Build Coastguard Worker     return 0;
64*288bf522SAndroid Build Coastguard Worker }
65*288bf522SAndroid Build Coastguard Worker 
66*288bf522SAndroid Build Coastguard Worker /* parses a 64-bit unsigned integer from string `src' into `dst' and if
67*288bf522SAndroid Build Coastguard Worker    `maxval' is >0, checks that `dst' <= `maxval' */
parse_uint64(const char * src,uint64_t maxval,uint64_t * dst)68*288bf522SAndroid Build Coastguard Worker static int parse_uint64(const char *src, uint64_t maxval, uint64_t *dst)
69*288bf522SAndroid Build Coastguard Worker {
70*288bf522SAndroid Build Coastguard Worker     char *end;
71*288bf522SAndroid Build Coastguard Worker     unsigned long long int value;
72*288bf522SAndroid Build Coastguard Worker 
73*288bf522SAndroid Build Coastguard Worker     check(src);
74*288bf522SAndroid Build Coastguard Worker     check(dst);
75*288bf522SAndroid Build Coastguard Worker 
76*288bf522SAndroid Build Coastguard Worker     errno = 0;
77*288bf522SAndroid Build Coastguard Worker     value = strtoull(src, &end, 0);
78*288bf522SAndroid Build Coastguard Worker 
79*288bf522SAndroid Build Coastguard Worker     if (*src == '\0' || *end != '\0' ||
80*288bf522SAndroid Build Coastguard Worker             (errno == ERANGE && value == ULLONG_MAX)) {
81*288bf522SAndroid Build Coastguard Worker         errno = EINVAL;
82*288bf522SAndroid Build Coastguard Worker         return -1;
83*288bf522SAndroid Build Coastguard Worker     }
84*288bf522SAndroid Build Coastguard Worker 
85*288bf522SAndroid Build Coastguard Worker     if (maxval && value > maxval) {
86*288bf522SAndroid Build Coastguard Worker         errno = EINVAL;
87*288bf522SAndroid Build Coastguard Worker         return -1;
88*288bf522SAndroid Build Coastguard Worker     }
89*288bf522SAndroid Build Coastguard Worker 
90*288bf522SAndroid Build Coastguard Worker    *dst = (uint64_t)value;
91*288bf522SAndroid Build Coastguard Worker     return 0;
92*288bf522SAndroid Build Coastguard Worker }
93*288bf522SAndroid Build Coastguard Worker 
94*288bf522SAndroid Build Coastguard Worker /* computes the size of verity hash tree for `file_size' bytes and returns the
95*288bf522SAndroid Build Coastguard Worker    number of hash tree levels in `verity_levels,' and the number of hashes per
96*288bf522SAndroid Build Coastguard Worker    level in `level_hashes', if the parameters are non-NULL */
verity_get_size(uint64_t file_size,uint32_t * verity_levels,uint32_t * level_hashes,uint32_t padded_digest_size)97*288bf522SAndroid Build Coastguard Worker uint64_t verity_get_size(uint64_t file_size, uint32_t *verity_levels,
98*288bf522SAndroid Build Coastguard Worker                          uint32_t *level_hashes, uint32_t padded_digest_size) {
99*288bf522SAndroid Build Coastguard Worker     // we assume a known metadata size, 4 KiB block size, and SHA-256 or SHA1 to
100*288bf522SAndroid Build Coastguard Worker     // avoid relying on disk content.
101*288bf522SAndroid Build Coastguard Worker 
102*288bf522SAndroid Build Coastguard Worker     uint32_t level = 0;
103*288bf522SAndroid Build Coastguard Worker     uint64_t total = 0;
104*288bf522SAndroid Build Coastguard Worker     uint64_t hashes = file_size / FEC_BLOCKSIZE;
105*288bf522SAndroid Build Coastguard Worker 
106*288bf522SAndroid Build Coastguard Worker     do {
107*288bf522SAndroid Build Coastguard Worker         if (level_hashes) {
108*288bf522SAndroid Build Coastguard Worker             level_hashes[level] = hashes;
109*288bf522SAndroid Build Coastguard Worker         }
110*288bf522SAndroid Build Coastguard Worker 
111*288bf522SAndroid Build Coastguard Worker         hashes = fec_div_round_up(hashes * padded_digest_size, FEC_BLOCKSIZE);
112*288bf522SAndroid Build Coastguard Worker         total += hashes;
113*288bf522SAndroid Build Coastguard Worker 
114*288bf522SAndroid Build Coastguard Worker         ++level;
115*288bf522SAndroid Build Coastguard Worker     } while (hashes > 1);
116*288bf522SAndroid Build Coastguard Worker 
117*288bf522SAndroid Build Coastguard Worker     if (verity_levels) {
118*288bf522SAndroid Build Coastguard Worker         *verity_levels = level;
119*288bf522SAndroid Build Coastguard Worker     }
120*288bf522SAndroid Build Coastguard Worker 
121*288bf522SAndroid Build Coastguard Worker     return total * FEC_BLOCKSIZE;
122*288bf522SAndroid Build Coastguard Worker }
123*288bf522SAndroid Build Coastguard Worker 
get_hash(const uint8_t * block,uint8_t * hash)124*288bf522SAndroid Build Coastguard Worker int hashtree_info::get_hash(const uint8_t *block, uint8_t *hash) {
125*288bf522SAndroid Build Coastguard Worker     auto md = EVP_get_digestbynid(nid_);
126*288bf522SAndroid Build Coastguard Worker     check(md);
127*288bf522SAndroid Build Coastguard Worker     auto mdctx = EVP_MD_CTX_new();
128*288bf522SAndroid Build Coastguard Worker     check(mdctx);
129*288bf522SAndroid Build Coastguard Worker 
130*288bf522SAndroid Build Coastguard Worker     EVP_DigestInit_ex(mdctx, md, nullptr);
131*288bf522SAndroid Build Coastguard Worker     EVP_DigestUpdate(mdctx, salt.data(), salt.size());
132*288bf522SAndroid Build Coastguard Worker     EVP_DigestUpdate(mdctx, block, FEC_BLOCKSIZE);
133*288bf522SAndroid Build Coastguard Worker     unsigned int hash_size;
134*288bf522SAndroid Build Coastguard Worker     EVP_DigestFinal_ex(mdctx, hash, &hash_size);
135*288bf522SAndroid Build Coastguard Worker     EVP_MD_CTX_free(mdctx);
136*288bf522SAndroid Build Coastguard Worker 
137*288bf522SAndroid Build Coastguard Worker     check(hash_size == digest_length_);
138*288bf522SAndroid Build Coastguard Worker     return 0;
139*288bf522SAndroid Build Coastguard Worker }
140*288bf522SAndroid Build Coastguard Worker 
initialize(uint64_t hash_start,uint64_t data_blocks,const std::vector<uint8_t> & salt,int nid)141*288bf522SAndroid Build Coastguard Worker int hashtree_info::initialize(uint64_t hash_start, uint64_t data_blocks,
142*288bf522SAndroid Build Coastguard Worker                               const std::vector<uint8_t> &salt, int nid) {
143*288bf522SAndroid Build Coastguard Worker     check(nid == NID_sha256 || nid == NID_sha1);
144*288bf522SAndroid Build Coastguard Worker 
145*288bf522SAndroid Build Coastguard Worker     this->hash_start = hash_start;
146*288bf522SAndroid Build Coastguard Worker     this->data_blocks = data_blocks;
147*288bf522SAndroid Build Coastguard Worker     this->salt = salt;
148*288bf522SAndroid Build Coastguard Worker     this->nid_ = nid;
149*288bf522SAndroid Build Coastguard Worker 
150*288bf522SAndroid Build Coastguard Worker     digest_length_ = nid == NID_sha1 ? SHA_DIGEST_LENGTH : SHA256_DIGEST_LENGTH;
151*288bf522SAndroid Build Coastguard Worker     // The padded digest size for both sha256 and sha1 are 256 bytes.
152*288bf522SAndroid Build Coastguard Worker     padded_digest_length_ = SHA256_DIGEST_LENGTH;
153*288bf522SAndroid Build Coastguard Worker 
154*288bf522SAndroid Build Coastguard Worker     return 0;
155*288bf522SAndroid Build Coastguard Worker }
156*288bf522SAndroid Build Coastguard Worker 
check_block_hash(const uint8_t * expected,const uint8_t * block)157*288bf522SAndroid Build Coastguard Worker bool hashtree_info::check_block_hash(const uint8_t *expected,
158*288bf522SAndroid Build Coastguard Worker                                      const uint8_t *block) {
159*288bf522SAndroid Build Coastguard Worker     check(block);
160*288bf522SAndroid Build Coastguard Worker     std::vector<uint8_t> hash(digest_length_, 0);
161*288bf522SAndroid Build Coastguard Worker 
162*288bf522SAndroid Build Coastguard Worker     if (unlikely(get_hash(block, hash.data()) == -1)) {
163*288bf522SAndroid Build Coastguard Worker         error("failed to hash");
164*288bf522SAndroid Build Coastguard Worker         return false;
165*288bf522SAndroid Build Coastguard Worker     }
166*288bf522SAndroid Build Coastguard Worker 
167*288bf522SAndroid Build Coastguard Worker     check(expected);
168*288bf522SAndroid Build Coastguard Worker     return !memcmp(expected, hash.data(), digest_length_);
169*288bf522SAndroid Build Coastguard Worker }
170*288bf522SAndroid Build Coastguard Worker 
check_block_hash_with_index(uint64_t index,const uint8_t * block)171*288bf522SAndroid Build Coastguard Worker bool hashtree_info::check_block_hash_with_index(uint64_t index,
172*288bf522SAndroid Build Coastguard Worker                                                 const uint8_t *block) {
173*288bf522SAndroid Build Coastguard Worker     check(index < data_blocks);
174*288bf522SAndroid Build Coastguard Worker 
175*288bf522SAndroid Build Coastguard Worker     const uint8_t *expected = &hash_data[index * padded_digest_length_];
176*288bf522SAndroid Build Coastguard Worker     return check_block_hash(expected, block);
177*288bf522SAndroid Build Coastguard Worker }
178*288bf522SAndroid Build Coastguard Worker 
179*288bf522SAndroid Build Coastguard Worker // Reads the hash and the corresponding data block using error correction, if
180*288bf522SAndroid Build Coastguard Worker // available.
ecc_read_hashes(fec_handle * f,uint64_t hash_offset,uint8_t * hash,uint64_t data_offset,uint8_t * data)181*288bf522SAndroid Build Coastguard Worker bool hashtree_info::ecc_read_hashes(fec_handle *f, uint64_t hash_offset,
182*288bf522SAndroid Build Coastguard Worker                                     uint8_t *hash, uint64_t data_offset,
183*288bf522SAndroid Build Coastguard Worker                                     uint8_t *data) {
184*288bf522SAndroid Build Coastguard Worker     check(f);
185*288bf522SAndroid Build Coastguard Worker 
186*288bf522SAndroid Build Coastguard Worker     if (hash &&
187*288bf522SAndroid Build Coastguard Worker         fec_pread(f, hash, digest_length_, hash_offset) != digest_length_) {
188*288bf522SAndroid Build Coastguard Worker         error("failed to read hash tree: offset %" PRIu64 ": %s", hash_offset,
189*288bf522SAndroid Build Coastguard Worker               strerror(errno));
190*288bf522SAndroid Build Coastguard Worker         return false;
191*288bf522SAndroid Build Coastguard Worker     }
192*288bf522SAndroid Build Coastguard Worker 
193*288bf522SAndroid Build Coastguard Worker     check(data);
194*288bf522SAndroid Build Coastguard Worker 
195*288bf522SAndroid Build Coastguard Worker     if (fec_pread(f, data, FEC_BLOCKSIZE, data_offset) != FEC_BLOCKSIZE) {
196*288bf522SAndroid Build Coastguard Worker         error("failed to read hash tree: data_offset %" PRIu64 ": %s",
197*288bf522SAndroid Build Coastguard Worker               data_offset, strerror(errno));
198*288bf522SAndroid Build Coastguard Worker         return false;
199*288bf522SAndroid Build Coastguard Worker     }
200*288bf522SAndroid Build Coastguard Worker 
201*288bf522SAndroid Build Coastguard Worker     return true;
202*288bf522SAndroid Build Coastguard Worker }
203*288bf522SAndroid Build Coastguard Worker 
verify_tree(const fec_handle * f,const uint8_t * root)204*288bf522SAndroid Build Coastguard Worker int hashtree_info::verify_tree(const fec_handle *f, const uint8_t *root) {
205*288bf522SAndroid Build Coastguard Worker     check(f);
206*288bf522SAndroid Build Coastguard Worker     check(root);
207*288bf522SAndroid Build Coastguard Worker 
208*288bf522SAndroid Build Coastguard Worker     uint8_t data[FEC_BLOCKSIZE];
209*288bf522SAndroid Build Coastguard Worker 
210*288bf522SAndroid Build Coastguard Worker     uint32_t levels = 0;
211*288bf522SAndroid Build Coastguard Worker 
212*288bf522SAndroid Build Coastguard Worker     /* calculate the size and the number of levels in the hash tree */
213*288bf522SAndroid Build Coastguard Worker     uint64_t hash_size = verity_get_size(data_blocks * FEC_BLOCKSIZE, &levels,
214*288bf522SAndroid Build Coastguard Worker                                          NULL, padded_digest_length_);
215*288bf522SAndroid Build Coastguard Worker 
216*288bf522SAndroid Build Coastguard Worker     check(hash_start < UINT64_MAX - hash_size);
217*288bf522SAndroid Build Coastguard Worker     check(hash_start + hash_size <= f->data_size);
218*288bf522SAndroid Build Coastguard Worker 
219*288bf522SAndroid Build Coastguard Worker     uint64_t hash_offset = hash_start;
220*288bf522SAndroid Build Coastguard Worker     uint64_t data_offset = hash_offset + FEC_BLOCKSIZE;
221*288bf522SAndroid Build Coastguard Worker 
222*288bf522SAndroid Build Coastguard Worker     /* validate the root hash */
223*288bf522SAndroid Build Coastguard Worker     if (!raw_pread(f->fd, data, FEC_BLOCKSIZE, hash_offset) ||
224*288bf522SAndroid Build Coastguard Worker         !check_block_hash(root, data)) {
225*288bf522SAndroid Build Coastguard Worker         /* try to correct */
226*288bf522SAndroid Build Coastguard Worker         if (!ecc_read_hashes(const_cast<fec_handle *>(f), 0, nullptr,
227*288bf522SAndroid Build Coastguard Worker                              hash_offset, data) ||
228*288bf522SAndroid Build Coastguard Worker             !check_block_hash(root, data)) {
229*288bf522SAndroid Build Coastguard Worker             error("root hash invalid");
230*288bf522SAndroid Build Coastguard Worker             return -1;
231*288bf522SAndroid Build Coastguard Worker         } else if (f->mode & O_RDWR &&
232*288bf522SAndroid Build Coastguard Worker                    !raw_pwrite(f->fd, data, FEC_BLOCKSIZE, hash_offset)) {
233*288bf522SAndroid Build Coastguard Worker             error("failed to rewrite the root block: %s", strerror(errno));
234*288bf522SAndroid Build Coastguard Worker             return -1;
235*288bf522SAndroid Build Coastguard Worker         }
236*288bf522SAndroid Build Coastguard Worker     }
237*288bf522SAndroid Build Coastguard Worker 
238*288bf522SAndroid Build Coastguard Worker     debug("root hash valid");
239*288bf522SAndroid Build Coastguard Worker 
240*288bf522SAndroid Build Coastguard Worker     /* calculate the number of hashes on each level */
241*288bf522SAndroid Build Coastguard Worker     uint32_t hashes[levels];
242*288bf522SAndroid Build Coastguard Worker 
243*288bf522SAndroid Build Coastguard Worker     verity_get_size(data_blocks * FEC_BLOCKSIZE, NULL, hashes,
244*288bf522SAndroid Build Coastguard Worker                     padded_digest_length_);
245*288bf522SAndroid Build Coastguard Worker 
246*288bf522SAndroid Build Coastguard Worker     uint64_t hash_data_offset = data_offset;
247*288bf522SAndroid Build Coastguard Worker     uint32_t hash_data_blocks = 0;
248*288bf522SAndroid Build Coastguard Worker     /* calculate the size and offset for the data hashes */
249*288bf522SAndroid Build Coastguard Worker     for (uint32_t i = 1; i < levels; ++i) {
250*288bf522SAndroid Build Coastguard Worker         uint32_t blocks = hashes[levels - i];
251*288bf522SAndroid Build Coastguard Worker         debug("%u hash blocks on level %u", blocks, levels - i);
252*288bf522SAndroid Build Coastguard Worker 
253*288bf522SAndroid Build Coastguard Worker         hash_data_offset = data_offset;
254*288bf522SAndroid Build Coastguard Worker         hash_data_blocks = blocks;
255*288bf522SAndroid Build Coastguard Worker 
256*288bf522SAndroid Build Coastguard Worker         data_offset += blocks * FEC_BLOCKSIZE;
257*288bf522SAndroid Build Coastguard Worker     }
258*288bf522SAndroid Build Coastguard Worker 
259*288bf522SAndroid Build Coastguard Worker     check(hash_data_blocks);
260*288bf522SAndroid Build Coastguard Worker     check(hash_data_blocks <= hash_size / FEC_BLOCKSIZE);
261*288bf522SAndroid Build Coastguard Worker 
262*288bf522SAndroid Build Coastguard Worker     check(hash_data_offset);
263*288bf522SAndroid Build Coastguard Worker     check(hash_data_offset <= UINT64_MAX - (hash_data_blocks * FEC_BLOCKSIZE));
264*288bf522SAndroid Build Coastguard Worker     check(hash_data_offset < f->data_size);
265*288bf522SAndroid Build Coastguard Worker     check(hash_data_offset + hash_data_blocks * FEC_BLOCKSIZE <= f->data_size);
266*288bf522SAndroid Build Coastguard Worker 
267*288bf522SAndroid Build Coastguard Worker     /* copy data hashes to memory in case they are corrupted, so we don't
268*288bf522SAndroid Build Coastguard Worker        have to correct them every time they are needed */
269*288bf522SAndroid Build Coastguard Worker     std::vector<uint8_t> data_hashes(hash_data_blocks * FEC_BLOCKSIZE, 0);
270*288bf522SAndroid Build Coastguard Worker 
271*288bf522SAndroid Build Coastguard Worker     /* validate the rest of the hash tree */
272*288bf522SAndroid Build Coastguard Worker     data_offset = hash_offset + FEC_BLOCKSIZE;
273*288bf522SAndroid Build Coastguard Worker 
274*288bf522SAndroid Build Coastguard Worker     std::vector<uint8_t> buffer(padded_digest_length_, 0);
275*288bf522SAndroid Build Coastguard Worker     for (uint32_t i = 1; i < levels; ++i) {
276*288bf522SAndroid Build Coastguard Worker         uint32_t blocks = hashes[levels - i];
277*288bf522SAndroid Build Coastguard Worker 
278*288bf522SAndroid Build Coastguard Worker         for (uint32_t j = 0; j < blocks; ++j) {
279*288bf522SAndroid Build Coastguard Worker             /* ecc reads are very I/O intensive, so read raw hash tree and do
280*288bf522SAndroid Build Coastguard Worker                error correcting only if it doesn't validate */
281*288bf522SAndroid Build Coastguard Worker             if (!raw_pread(f->fd, buffer.data(), padded_digest_length_,
282*288bf522SAndroid Build Coastguard Worker                            hash_offset + j * padded_digest_length_) ||
283*288bf522SAndroid Build Coastguard Worker                 !raw_pread(f->fd, data, FEC_BLOCKSIZE,
284*288bf522SAndroid Build Coastguard Worker                            data_offset + j * FEC_BLOCKSIZE)) {
285*288bf522SAndroid Build Coastguard Worker                 error("failed to read hashes: %s", strerror(errno));
286*288bf522SAndroid Build Coastguard Worker                 return -1;
287*288bf522SAndroid Build Coastguard Worker             }
288*288bf522SAndroid Build Coastguard Worker 
289*288bf522SAndroid Build Coastguard Worker             if (!check_block_hash(buffer.data(), data)) {
290*288bf522SAndroid Build Coastguard Worker                 /* try to correct */
291*288bf522SAndroid Build Coastguard Worker                 if (!ecc_read_hashes(const_cast<fec_handle *>(f),
292*288bf522SAndroid Build Coastguard Worker                                      hash_offset + j * padded_digest_length_,
293*288bf522SAndroid Build Coastguard Worker                                      buffer.data(),
294*288bf522SAndroid Build Coastguard Worker                                      data_offset + j * FEC_BLOCKSIZE, data) ||
295*288bf522SAndroid Build Coastguard Worker                     !check_block_hash(buffer.data(), data)) {
296*288bf522SAndroid Build Coastguard Worker                     error("invalid hash tree: hash_offset %" PRIu64
297*288bf522SAndroid Build Coastguard Worker                           ", "
298*288bf522SAndroid Build Coastguard Worker                           "data_offset %" PRIu64 ", block %u",
299*288bf522SAndroid Build Coastguard Worker                           hash_offset, data_offset, j);
300*288bf522SAndroid Build Coastguard Worker                     return -1;
301*288bf522SAndroid Build Coastguard Worker                 }
302*288bf522SAndroid Build Coastguard Worker 
303*288bf522SAndroid Build Coastguard Worker                 /* update the corrected blocks to the file if we are in r/w
304*288bf522SAndroid Build Coastguard Worker                    mode */
305*288bf522SAndroid Build Coastguard Worker                 if (f->mode & O_RDWR) {
306*288bf522SAndroid Build Coastguard Worker                     if (!raw_pwrite(f->fd, buffer.data(), padded_digest_length_,
307*288bf522SAndroid Build Coastguard Worker                                     hash_offset + j * padded_digest_length_) ||
308*288bf522SAndroid Build Coastguard Worker                         !raw_pwrite(f->fd, data, FEC_BLOCKSIZE,
309*288bf522SAndroid Build Coastguard Worker                                     data_offset + j * FEC_BLOCKSIZE)) {
310*288bf522SAndroid Build Coastguard Worker                         error("failed to write hashes: %s", strerror(errno));
311*288bf522SAndroid Build Coastguard Worker                         return -1;
312*288bf522SAndroid Build Coastguard Worker                     }
313*288bf522SAndroid Build Coastguard Worker                 }
314*288bf522SAndroid Build Coastguard Worker             }
315*288bf522SAndroid Build Coastguard Worker 
316*288bf522SAndroid Build Coastguard Worker             if (blocks == hash_data_blocks) {
317*288bf522SAndroid Build Coastguard Worker                 std::copy(data, data + FEC_BLOCKSIZE,
318*288bf522SAndroid Build Coastguard Worker                           data_hashes.begin() + j * FEC_BLOCKSIZE);
319*288bf522SAndroid Build Coastguard Worker             }
320*288bf522SAndroid Build Coastguard Worker         }
321*288bf522SAndroid Build Coastguard Worker 
322*288bf522SAndroid Build Coastguard Worker         hash_offset = data_offset;
323*288bf522SAndroid Build Coastguard Worker         data_offset += blocks * FEC_BLOCKSIZE;
324*288bf522SAndroid Build Coastguard Worker     }
325*288bf522SAndroid Build Coastguard Worker 
326*288bf522SAndroid Build Coastguard Worker     debug("valid");
327*288bf522SAndroid Build Coastguard Worker 
328*288bf522SAndroid Build Coastguard Worker     this->hash_data = std::move(data_hashes);
329*288bf522SAndroid Build Coastguard Worker 
330*288bf522SAndroid Build Coastguard Worker     std::vector<uint8_t> zero_block(FEC_BLOCKSIZE, 0);
331*288bf522SAndroid Build Coastguard Worker     zero_hash.resize(padded_digest_length_, 0);
332*288bf522SAndroid Build Coastguard Worker     if (get_hash(zero_block.data(), zero_hash.data()) == -1) {
333*288bf522SAndroid Build Coastguard Worker         error("failed to hash");
334*288bf522SAndroid Build Coastguard Worker         return -1;
335*288bf522SAndroid Build Coastguard Worker     }
336*288bf522SAndroid Build Coastguard Worker     return 0;
337*288bf522SAndroid Build Coastguard Worker }
338*288bf522SAndroid Build Coastguard Worker 
339*288bf522SAndroid Build Coastguard Worker /* reads, corrects and parses the verity table, validates parameters, and if
340*288bf522SAndroid Build Coastguard Worker    `f->flags' does not have `FEC_VERITY_DISABLE' set, calls `verify_tree' to
341*288bf522SAndroid Build Coastguard Worker    load and validate the hash tree */
parse_table(fec_handle * f,uint64_t offset,uint32_t size,bool useecc)342*288bf522SAndroid Build Coastguard Worker static int parse_table(fec_handle *f, uint64_t offset, uint32_t size, bool useecc)
343*288bf522SAndroid Build Coastguard Worker {
344*288bf522SAndroid Build Coastguard Worker     check(f);
345*288bf522SAndroid Build Coastguard Worker     check(size >= VERITY_MIN_TABLE_SIZE);
346*288bf522SAndroid Build Coastguard Worker     check(size <= VERITY_MAX_TABLE_SIZE);
347*288bf522SAndroid Build Coastguard Worker 
348*288bf522SAndroid Build Coastguard Worker     debug("offset = %" PRIu64 ", size = %u", offset, size);
349*288bf522SAndroid Build Coastguard Worker 
350*288bf522SAndroid Build Coastguard Worker     std::string table(size, 0);
351*288bf522SAndroid Build Coastguard Worker 
352*288bf522SAndroid Build Coastguard Worker     if (!useecc) {
353*288bf522SAndroid Build Coastguard Worker         if (!raw_pread(f->fd, const_cast<char *>(table.data()), size, offset)) {
354*288bf522SAndroid Build Coastguard Worker             error("failed to read verity table: %s", strerror(errno));
355*288bf522SAndroid Build Coastguard Worker             return -1;
356*288bf522SAndroid Build Coastguard Worker         }
357*288bf522SAndroid Build Coastguard Worker     } else if (fec_pread(f, const_cast<char *>(table.data()), size, offset) !=
358*288bf522SAndroid Build Coastguard Worker                (ssize_t)size) {
359*288bf522SAndroid Build Coastguard Worker         error("failed to ecc read verity table: %s", strerror(errno));
360*288bf522SAndroid Build Coastguard Worker         return -1;
361*288bf522SAndroid Build Coastguard Worker     }
362*288bf522SAndroid Build Coastguard Worker 
363*288bf522SAndroid Build Coastguard Worker     debug("verity table: '%s'", table.c_str());
364*288bf522SAndroid Build Coastguard Worker 
365*288bf522SAndroid Build Coastguard Worker     int i = 0;
366*288bf522SAndroid Build Coastguard Worker     std::vector<uint8_t> salt;
367*288bf522SAndroid Build Coastguard Worker     uint8_t root[SHA256_DIGEST_LENGTH];
368*288bf522SAndroid Build Coastguard Worker     uint64_t hash_start = 0;
369*288bf522SAndroid Build Coastguard Worker     uint64_t data_blocks = 0;
370*288bf522SAndroid Build Coastguard Worker 
371*288bf522SAndroid Build Coastguard Worker     auto tokens = android::base::Split(table, " ");
372*288bf522SAndroid Build Coastguard Worker 
373*288bf522SAndroid Build Coastguard Worker     for (const auto& token : tokens) {
374*288bf522SAndroid Build Coastguard Worker         switch (i++) {
375*288bf522SAndroid Build Coastguard Worker         case 0: /* version */
376*288bf522SAndroid Build Coastguard Worker             if (token != stringify(VERITY_TABLE_VERSION)) {
377*288bf522SAndroid Build Coastguard Worker                 error("unsupported verity table version: %s", token.c_str());
378*288bf522SAndroid Build Coastguard Worker                 return -1;
379*288bf522SAndroid Build Coastguard Worker             }
380*288bf522SAndroid Build Coastguard Worker             break;
381*288bf522SAndroid Build Coastguard Worker         case 3: /* data_block_size */
382*288bf522SAndroid Build Coastguard Worker         case 4: /* hash_block_size */
383*288bf522SAndroid Build Coastguard Worker             /* assume 4 KiB block sizes for everything */
384*288bf522SAndroid Build Coastguard Worker             if (token != stringify(FEC_BLOCKSIZE)) {
385*288bf522SAndroid Build Coastguard Worker                 error("unsupported verity block size: %s", token.c_str());
386*288bf522SAndroid Build Coastguard Worker                 return -1;
387*288bf522SAndroid Build Coastguard Worker             }
388*288bf522SAndroid Build Coastguard Worker             break;
389*288bf522SAndroid Build Coastguard Worker         case 5: /* num_data_blocks */
390*288bf522SAndroid Build Coastguard Worker             if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
391*288bf522SAndroid Build Coastguard Worker                              &data_blocks) == -1) {
392*288bf522SAndroid Build Coastguard Worker                 error("invalid number of verity data blocks: %s",
393*288bf522SAndroid Build Coastguard Worker                     token.c_str());
394*288bf522SAndroid Build Coastguard Worker                 return -1;
395*288bf522SAndroid Build Coastguard Worker             }
396*288bf522SAndroid Build Coastguard Worker             break;
397*288bf522SAndroid Build Coastguard Worker         case 6: /* hash_start_block */
398*288bf522SAndroid Build Coastguard Worker             if (parse_uint64(token.c_str(), f->data_size / FEC_BLOCKSIZE,
399*288bf522SAndroid Build Coastguard Worker                              &hash_start) == -1) {
400*288bf522SAndroid Build Coastguard Worker                 error("invalid verity hash start block: %s", token.c_str());
401*288bf522SAndroid Build Coastguard Worker                 return -1;
402*288bf522SAndroid Build Coastguard Worker             }
403*288bf522SAndroid Build Coastguard Worker 
404*288bf522SAndroid Build Coastguard Worker             hash_start *= FEC_BLOCKSIZE;
405*288bf522SAndroid Build Coastguard Worker             break;
406*288bf522SAndroid Build Coastguard Worker         case 7: /* algorithm */
407*288bf522SAndroid Build Coastguard Worker             if (token != "sha256") {
408*288bf522SAndroid Build Coastguard Worker                 error("unsupported verity hash algorithm: %s", token.c_str());
409*288bf522SAndroid Build Coastguard Worker                 return -1;
410*288bf522SAndroid Build Coastguard Worker             }
411*288bf522SAndroid Build Coastguard Worker             break;
412*288bf522SAndroid Build Coastguard Worker         case 8: /* digest */
413*288bf522SAndroid Build Coastguard Worker             if (parse_hex(root, sizeof(root), token.c_str()) == -1) {
414*288bf522SAndroid Build Coastguard Worker                 error("invalid verity root hash: %s", token.c_str());
415*288bf522SAndroid Build Coastguard Worker                 return -1;
416*288bf522SAndroid Build Coastguard Worker             }
417*288bf522SAndroid Build Coastguard Worker             break;
418*288bf522SAndroid Build Coastguard Worker         case 9: /* salt */
419*288bf522SAndroid Build Coastguard Worker         {
420*288bf522SAndroid Build Coastguard Worker             uint32_t salt_size = token.size();
421*288bf522SAndroid Build Coastguard Worker             check(salt_size % 2 == 0);
422*288bf522SAndroid Build Coastguard Worker             salt_size /= 2;
423*288bf522SAndroid Build Coastguard Worker 
424*288bf522SAndroid Build Coastguard Worker             salt.resize(salt_size, 0);
425*288bf522SAndroid Build Coastguard Worker 
426*288bf522SAndroid Build Coastguard Worker             if (parse_hex(salt.data(), salt_size, token.c_str()) == -1) {
427*288bf522SAndroid Build Coastguard Worker                 error("invalid verity salt: %s", token.c_str());
428*288bf522SAndroid Build Coastguard Worker                 return -1;
429*288bf522SAndroid Build Coastguard Worker             }
430*288bf522SAndroid Build Coastguard Worker             break;
431*288bf522SAndroid Build Coastguard Worker         }
432*288bf522SAndroid Build Coastguard Worker         default:
433*288bf522SAndroid Build Coastguard Worker             break;
434*288bf522SAndroid Build Coastguard Worker         }
435*288bf522SAndroid Build Coastguard Worker     }
436*288bf522SAndroid Build Coastguard Worker 
437*288bf522SAndroid Build Coastguard Worker     if (i < VERITY_TABLE_ARGS) {
438*288bf522SAndroid Build Coastguard Worker         error("not enough arguments in verity table: %d; expected at least "
439*288bf522SAndroid Build Coastguard Worker             stringify(VERITY_TABLE_ARGS), i);
440*288bf522SAndroid Build Coastguard Worker         return -1;
441*288bf522SAndroid Build Coastguard Worker     }
442*288bf522SAndroid Build Coastguard Worker 
443*288bf522SAndroid Build Coastguard Worker     check(hash_start < f->data_size);
444*288bf522SAndroid Build Coastguard Worker 
445*288bf522SAndroid Build Coastguard Worker     verity_info *v = &f->verity;
446*288bf522SAndroid Build Coastguard Worker     if (v->metadata_start < hash_start) {
447*288bf522SAndroid Build Coastguard Worker         check(data_blocks == v->metadata_start / FEC_BLOCKSIZE);
448*288bf522SAndroid Build Coastguard Worker     } else {
449*288bf522SAndroid Build Coastguard Worker         check(data_blocks == hash_start / FEC_BLOCKSIZE);
450*288bf522SAndroid Build Coastguard Worker     }
451*288bf522SAndroid Build Coastguard Worker 
452*288bf522SAndroid Build Coastguard Worker     v->table = std::move(table);
453*288bf522SAndroid Build Coastguard Worker 
454*288bf522SAndroid Build Coastguard Worker     v->hashtree.initialize(hash_start, data_blocks, salt, NID_sha256);
455*288bf522SAndroid Build Coastguard Worker     if (!(f->flags & FEC_VERITY_DISABLE)) {
456*288bf522SAndroid Build Coastguard Worker         if (v->hashtree.verify_tree(f, root) == -1) {
457*288bf522SAndroid Build Coastguard Worker             return -1;
458*288bf522SAndroid Build Coastguard Worker         }
459*288bf522SAndroid Build Coastguard Worker 
460*288bf522SAndroid Build Coastguard Worker         check(!v->hashtree.hash_data.empty());
461*288bf522SAndroid Build Coastguard Worker         check(!v->hashtree.zero_hash.empty());
462*288bf522SAndroid Build Coastguard Worker     }
463*288bf522SAndroid Build Coastguard Worker 
464*288bf522SAndroid Build Coastguard Worker     return 0;
465*288bf522SAndroid Build Coastguard Worker }
466*288bf522SAndroid Build Coastguard Worker 
467*288bf522SAndroid Build Coastguard Worker /* rewrites verity metadata block using error corrected data in `f->verity' */
rewrite_metadata(fec_handle * f,uint64_t offset)468*288bf522SAndroid Build Coastguard Worker static int rewrite_metadata(fec_handle *f, uint64_t offset)
469*288bf522SAndroid Build Coastguard Worker {
470*288bf522SAndroid Build Coastguard Worker     check(f);
471*288bf522SAndroid Build Coastguard Worker     check(f->data_size > VERITY_METADATA_SIZE);
472*288bf522SAndroid Build Coastguard Worker     check(offset <= f->data_size - VERITY_METADATA_SIZE);
473*288bf522SAndroid Build Coastguard Worker 
474*288bf522SAndroid Build Coastguard Worker     std::unique_ptr<uint8_t[]> metadata(
475*288bf522SAndroid Build Coastguard Worker         new (std::nothrow) uint8_t[VERITY_METADATA_SIZE]);
476*288bf522SAndroid Build Coastguard Worker 
477*288bf522SAndroid Build Coastguard Worker     if (!metadata) {
478*288bf522SAndroid Build Coastguard Worker         errno = ENOMEM;
479*288bf522SAndroid Build Coastguard Worker         return -1;
480*288bf522SAndroid Build Coastguard Worker     }
481*288bf522SAndroid Build Coastguard Worker 
482*288bf522SAndroid Build Coastguard Worker     memset(metadata.get(), 0, VERITY_METADATA_SIZE);
483*288bf522SAndroid Build Coastguard Worker 
484*288bf522SAndroid Build Coastguard Worker     verity_info *v = &f->verity;
485*288bf522SAndroid Build Coastguard Worker     memcpy(metadata.get(), &v->header, sizeof(v->header));
486*288bf522SAndroid Build Coastguard Worker 
487*288bf522SAndroid Build Coastguard Worker     check(!v->table.empty());
488*288bf522SAndroid Build Coastguard Worker     size_t len = v->table.size();
489*288bf522SAndroid Build Coastguard Worker 
490*288bf522SAndroid Build Coastguard Worker     check(sizeof(v->header) + len <= VERITY_METADATA_SIZE);
491*288bf522SAndroid Build Coastguard Worker     memcpy(metadata.get() + sizeof(v->header), v->table.data(), len);
492*288bf522SAndroid Build Coastguard Worker 
493*288bf522SAndroid Build Coastguard Worker     return raw_pwrite(f->fd, metadata.get(), VERITY_METADATA_SIZE, offset);
494*288bf522SAndroid Build Coastguard Worker }
495*288bf522SAndroid Build Coastguard Worker 
validate_header(const fec_handle * f,const verity_header * header,uint64_t offset)496*288bf522SAndroid Build Coastguard Worker static int validate_header(const fec_handle *f, const verity_header *header,
497*288bf522SAndroid Build Coastguard Worker         uint64_t offset)
498*288bf522SAndroid Build Coastguard Worker {
499*288bf522SAndroid Build Coastguard Worker     check(f);
500*288bf522SAndroid Build Coastguard Worker     check(header);
501*288bf522SAndroid Build Coastguard Worker 
502*288bf522SAndroid Build Coastguard Worker     if (header->magic != VERITY_MAGIC &&
503*288bf522SAndroid Build Coastguard Worker         header->magic != VERITY_MAGIC_DISABLE) {
504*288bf522SAndroid Build Coastguard Worker         return -1;
505*288bf522SAndroid Build Coastguard Worker     }
506*288bf522SAndroid Build Coastguard Worker 
507*288bf522SAndroid Build Coastguard Worker     if (header->version != VERITY_VERSION) {
508*288bf522SAndroid Build Coastguard Worker         error("unsupported verity version %u", header->version);
509*288bf522SAndroid Build Coastguard Worker         return -1;
510*288bf522SAndroid Build Coastguard Worker     }
511*288bf522SAndroid Build Coastguard Worker 
512*288bf522SAndroid Build Coastguard Worker     if (header->length < VERITY_MIN_TABLE_SIZE ||
513*288bf522SAndroid Build Coastguard Worker         header->length > VERITY_MAX_TABLE_SIZE) {
514*288bf522SAndroid Build Coastguard Worker         error("invalid verity table size: %u; expected ["
515*288bf522SAndroid Build Coastguard Worker             stringify(VERITY_MIN_TABLE_SIZE) ", "
516*288bf522SAndroid Build Coastguard Worker             stringify(VERITY_MAX_TABLE_SIZE) ")", header->length);
517*288bf522SAndroid Build Coastguard Worker         return -1;
518*288bf522SAndroid Build Coastguard Worker     }
519*288bf522SAndroid Build Coastguard Worker 
520*288bf522SAndroid Build Coastguard Worker     /* signature is skipped, because for our purposes it won't matter from
521*288bf522SAndroid Build Coastguard Worker        where the data originates; the caller of the library is responsible
522*288bf522SAndroid Build Coastguard Worker        for signature verification */
523*288bf522SAndroid Build Coastguard Worker 
524*288bf522SAndroid Build Coastguard Worker     if (offset > UINT64_MAX - header->length) {
525*288bf522SAndroid Build Coastguard Worker         error("invalid verity table length: %u", header->length);
526*288bf522SAndroid Build Coastguard Worker         return -1;
527*288bf522SAndroid Build Coastguard Worker     } else if (offset + header->length >= f->data_size) {
528*288bf522SAndroid Build Coastguard Worker         error("invalid verity table length: %u", header->length);
529*288bf522SAndroid Build Coastguard Worker         return -1;
530*288bf522SAndroid Build Coastguard Worker     }
531*288bf522SAndroid Build Coastguard Worker 
532*288bf522SAndroid Build Coastguard Worker     return 0;
533*288bf522SAndroid Build Coastguard Worker }
534*288bf522SAndroid Build Coastguard Worker 
535*288bf522SAndroid Build Coastguard Worker /* attempts to read verity metadata from `f->fd' position `offset'; if in r/w
536*288bf522SAndroid Build Coastguard Worker    mode, rewrites the metadata if it had errors */
verity_parse_header(fec_handle * f,uint64_t offset)537*288bf522SAndroid Build Coastguard Worker int verity_parse_header(fec_handle *f, uint64_t offset)
538*288bf522SAndroid Build Coastguard Worker {
539*288bf522SAndroid Build Coastguard Worker     check(f);
540*288bf522SAndroid Build Coastguard Worker     check(f->data_size > VERITY_METADATA_SIZE);
541*288bf522SAndroid Build Coastguard Worker 
542*288bf522SAndroid Build Coastguard Worker     if (offset > f->data_size - VERITY_METADATA_SIZE) {
543*288bf522SAndroid Build Coastguard Worker         debug("failed to read verity header: offset %" PRIu64 " is too far",
544*288bf522SAndroid Build Coastguard Worker             offset);
545*288bf522SAndroid Build Coastguard Worker         return -1;
546*288bf522SAndroid Build Coastguard Worker     }
547*288bf522SAndroid Build Coastguard Worker 
548*288bf522SAndroid Build Coastguard Worker     verity_info *v = &f->verity;
549*288bf522SAndroid Build Coastguard Worker     uint64_t errors = f->errors;
550*288bf522SAndroid Build Coastguard Worker 
551*288bf522SAndroid Build Coastguard Worker     if (!raw_pread(f->fd, &v->header, sizeof(v->header), offset)) {
552*288bf522SAndroid Build Coastguard Worker         error("failed to read verity header: %s", strerror(errno));
553*288bf522SAndroid Build Coastguard Worker         return -1;
554*288bf522SAndroid Build Coastguard Worker     }
555*288bf522SAndroid Build Coastguard Worker 
556*288bf522SAndroid Build Coastguard Worker     /* use raw data to check for the alternative magic, because it will
557*288bf522SAndroid Build Coastguard Worker        be error corrected to VERITY_MAGIC otherwise */
558*288bf522SAndroid Build Coastguard Worker     if (v->header.magic == VERITY_MAGIC_DISABLE) {
559*288bf522SAndroid Build Coastguard Worker         /* this value is not used by us, but can be used by a caller to
560*288bf522SAndroid Build Coastguard Worker            decide whether dm-verity should be enabled */
561*288bf522SAndroid Build Coastguard Worker         v->disabled = true;
562*288bf522SAndroid Build Coastguard Worker     }
563*288bf522SAndroid Build Coastguard Worker 
564*288bf522SAndroid Build Coastguard Worker     if (fec_pread(f, &v->ecc_header, sizeof(v->ecc_header), offset) !=
565*288bf522SAndroid Build Coastguard Worker             sizeof(v->ecc_header)) {
566*288bf522SAndroid Build Coastguard Worker         warn("failed to read verity header: %s", strerror(errno));
567*288bf522SAndroid Build Coastguard Worker         return -1;
568*288bf522SAndroid Build Coastguard Worker     }
569*288bf522SAndroid Build Coastguard Worker 
570*288bf522SAndroid Build Coastguard Worker     if (validate_header(f, &v->header, offset)) {
571*288bf522SAndroid Build Coastguard Worker         /* raw verity header is invalid; this could be due to corruption, or
572*288bf522SAndroid Build Coastguard Worker            due to missing verity metadata */
573*288bf522SAndroid Build Coastguard Worker 
574*288bf522SAndroid Build Coastguard Worker         if (validate_header(f, &v->ecc_header, offset)) {
575*288bf522SAndroid Build Coastguard Worker             return -1; /* either way, we cannot recover */
576*288bf522SAndroid Build Coastguard Worker         }
577*288bf522SAndroid Build Coastguard Worker 
578*288bf522SAndroid Build Coastguard Worker         /* report mismatching fields */
579*288bf522SAndroid Build Coastguard Worker         if (!v->disabled && v->header.magic != v->ecc_header.magic) {
580*288bf522SAndroid Build Coastguard Worker             warn("corrected verity header magic");
581*288bf522SAndroid Build Coastguard Worker             v->header.magic = v->ecc_header.magic;
582*288bf522SAndroid Build Coastguard Worker         }
583*288bf522SAndroid Build Coastguard Worker 
584*288bf522SAndroid Build Coastguard Worker         if (v->header.version != v->ecc_header.version) {
585*288bf522SAndroid Build Coastguard Worker             warn("corrected verity header version");
586*288bf522SAndroid Build Coastguard Worker             v->header.version = v->ecc_header.version;
587*288bf522SAndroid Build Coastguard Worker         }
588*288bf522SAndroid Build Coastguard Worker 
589*288bf522SAndroid Build Coastguard Worker         if (v->header.length != v->ecc_header.length) {
590*288bf522SAndroid Build Coastguard Worker             warn("corrected verity header length");
591*288bf522SAndroid Build Coastguard Worker             v->header.length = v->ecc_header.length;
592*288bf522SAndroid Build Coastguard Worker         }
593*288bf522SAndroid Build Coastguard Worker 
594*288bf522SAndroid Build Coastguard Worker         if (memcmp(v->header.signature, v->ecc_header.signature,
595*288bf522SAndroid Build Coastguard Worker                 sizeof(v->header.signature))) {
596*288bf522SAndroid Build Coastguard Worker             warn("corrected verity header signature");
597*288bf522SAndroid Build Coastguard Worker             /* we have no way of knowing which signature is correct, if either
598*288bf522SAndroid Build Coastguard Worker                of them is */
599*288bf522SAndroid Build Coastguard Worker         }
600*288bf522SAndroid Build Coastguard Worker     }
601*288bf522SAndroid Build Coastguard Worker 
602*288bf522SAndroid Build Coastguard Worker     v->metadata_start = offset;
603*288bf522SAndroid Build Coastguard Worker 
604*288bf522SAndroid Build Coastguard Worker     if (parse_table(f, offset + sizeof(v->header), v->header.length,
605*288bf522SAndroid Build Coastguard Worker             false) == -1 &&
606*288bf522SAndroid Build Coastguard Worker         parse_table(f, offset + sizeof(v->header), v->header.length,
607*288bf522SAndroid Build Coastguard Worker             true)  == -1) {
608*288bf522SAndroid Build Coastguard Worker         return -1;
609*288bf522SAndroid Build Coastguard Worker     }
610*288bf522SAndroid Build Coastguard Worker 
611*288bf522SAndroid Build Coastguard Worker     /* if we corrected something while parsing metadata and we are in r/w
612*288bf522SAndroid Build Coastguard Worker        mode, rewrite the corrected metadata */
613*288bf522SAndroid Build Coastguard Worker     if (f->mode & O_RDWR && f->errors > errors &&
614*288bf522SAndroid Build Coastguard Worker             rewrite_metadata(f, offset) < 0) {
615*288bf522SAndroid Build Coastguard Worker         warn("failed to rewrite verity metadata: %s", strerror(errno));
616*288bf522SAndroid Build Coastguard Worker     }
617*288bf522SAndroid Build Coastguard Worker 
618*288bf522SAndroid Build Coastguard Worker     if (v->metadata_start < v->hashtree.hash_start) {
619*288bf522SAndroid Build Coastguard Worker         f->data_size = v->metadata_start;
620*288bf522SAndroid Build Coastguard Worker     } else {
621*288bf522SAndroid Build Coastguard Worker         f->data_size = v->hashtree.hash_start;
622*288bf522SAndroid Build Coastguard Worker     }
623*288bf522SAndroid Build Coastguard Worker 
624*288bf522SAndroid Build Coastguard Worker     return 0;
625*288bf522SAndroid Build Coastguard Worker }
626*288bf522SAndroid Build Coastguard Worker 
fec_verity_set_status(struct fec_handle * f,bool enabled)627*288bf522SAndroid Build Coastguard Worker int fec_verity_set_status(struct fec_handle *f, bool enabled)
628*288bf522SAndroid Build Coastguard Worker {
629*288bf522SAndroid Build Coastguard Worker     check(f);
630*288bf522SAndroid Build Coastguard Worker 
631*288bf522SAndroid Build Coastguard Worker     if (!(f->mode & O_RDWR)) {
632*288bf522SAndroid Build Coastguard Worker         error("cannot update verity magic: read-only handle");
633*288bf522SAndroid Build Coastguard Worker         errno = EBADF;
634*288bf522SAndroid Build Coastguard Worker         return -1;
635*288bf522SAndroid Build Coastguard Worker     }
636*288bf522SAndroid Build Coastguard Worker 
637*288bf522SAndroid Build Coastguard Worker     verity_info *v = &f->verity;
638*288bf522SAndroid Build Coastguard Worker 
639*288bf522SAndroid Build Coastguard Worker     if (!v->metadata_start) {
640*288bf522SAndroid Build Coastguard Worker         error("cannot update verity magic: no metadata found");
641*288bf522SAndroid Build Coastguard Worker         errno = EINVAL;
642*288bf522SAndroid Build Coastguard Worker         return -1;
643*288bf522SAndroid Build Coastguard Worker     }
644*288bf522SAndroid Build Coastguard Worker 
645*288bf522SAndroid Build Coastguard Worker     if (v->disabled == !enabled) {
646*288bf522SAndroid Build Coastguard Worker         return 0; /* nothing to do */
647*288bf522SAndroid Build Coastguard Worker     }
648*288bf522SAndroid Build Coastguard Worker 
649*288bf522SAndroid Build Coastguard Worker     uint32_t magic = enabled ? VERITY_MAGIC : VERITY_MAGIC_DISABLE;
650*288bf522SAndroid Build Coastguard Worker 
651*288bf522SAndroid Build Coastguard Worker     if (!raw_pwrite(f->fd, &magic, sizeof(magic), v->metadata_start)) {
652*288bf522SAndroid Build Coastguard Worker         error("failed to update verity magic to %08x: %s", magic,
653*288bf522SAndroid Build Coastguard Worker               strerror(errno));
654*288bf522SAndroid Build Coastguard Worker         return -1;
655*288bf522SAndroid Build Coastguard Worker     }
656*288bf522SAndroid Build Coastguard Worker 
657*288bf522SAndroid Build Coastguard Worker     warn("updated verity magic to %08x (%s)", magic,
658*288bf522SAndroid Build Coastguard Worker         enabled ? "enabled" : "disabled");
659*288bf522SAndroid Build Coastguard Worker     v->disabled = !enabled;
660*288bf522SAndroid Build Coastguard Worker 
661*288bf522SAndroid Build Coastguard Worker     return 0;
662*288bf522SAndroid Build Coastguard Worker }
663