1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "fs_hvb.h"
17 #include "fs_dm.h"
18 #include "securec.h"
19 #include <stdint.h>
20 #include "beget_ext.h"
21 #include "init_utils.h"
22 #include <libhvb.h>
23 #include <libgen.h>
24
25 #ifdef __cplusplus
26 #if __cplusplus
27 extern "C" {
28 #endif
29 #endif
30
31 #define FS_HVB_VERITY_TARGET_MAX 512
32 #define FS_HVB_BLANK_SPACE_ASCII 32
33 #define FS_HVB_SECTOR_BYTES 512
34 #define FS_HVB_UINT64_MAX_LENGTH 64
35 #define FS_HVB_HASH_ALG_STR_MAX 32
36
37 #define FS_HVB_DIGEST_SHA256_BYTES 32
38 #define FS_HVB_DIGEST_SHA512_BYTES 64
39 #define FS_HVB_DIGEST_MAX_BYTES FS_HVB_DIGEST_SHA512_BYTES
40 #define FS_HVB_DIGEST_STR_MAX_BYTES 128
41 #define FS_HVB_DEVPATH_MAX_LEN 128
42 #define FS_HVB_RVT_PARTITION_NAME "rvt"
43 #define FS_HVB_CMDLINE_PATH "/proc/cmdline"
44 #define FS_HVB_PARTITION_PREFIX "/dev/block/by-name/"
45
46 #define FS_HVB_RETURN_ERR_IF_NULL(__ptr) \
47 do { \
48 if ((__ptr) == NULL) { \
49 BEGET_LOGE("error, %s is NULL\n", #__ptr); \
50 return -1; \
51 } \
52 } while (0)
53
54 static struct hvb_verified_data *g_vd = NULL;
55
FsHvbBinToHexChar(char octet)56 static char FsHvbBinToHexChar(char octet)
57 {
58 return octet < 10 ? '0' + octet : 'a' + (octet - 10);
59 }
60
FsHvbHexCharToBin(char hex)61 static char FsHvbHexCharToBin(char hex)
62 {
63 return '0' <= hex && hex <= '9' ? hex - '0' : 10 + (hex - 'a');
64 }
65
FsHvbGetHashStr(char * str,size_t size)66 static int FsHvbGetHashStr(char *str, size_t size)
67 {
68 return FsHvbGetValueFromCmdLine(str, size, HVB_CMDLINE_HASH_ALG);
69 }
70
FsHvbGetCertDigstStr(char * str,size_t size)71 static int FsHvbGetCertDigstStr(char *str, size_t size)
72 {
73 return FsHvbGetValueFromCmdLine(str, size, HVB_CMDLINE_CERT_DIGEST);
74 }
75
FsHvbComputeSha256(char * digest,size_t size,struct hvb_cert_data * certs,uint64_t num_certs)76 static int FsHvbComputeSha256(char *digest, size_t size, struct hvb_cert_data *certs, uint64_t num_certs)
77 {
78 int ret;
79 uint64_t n;
80 struct hash_ctx_t ctx = {0};
81
82 if (size < FS_HVB_DIGEST_SHA256_BYTES) {
83 BEGET_LOGE("error, size=%zu", size);
84 return -1;
85 }
86
87 ret = hash_ctx_init(&ctx, HASH_ALG_SHA256);
88 if (ret != HASH_OK) {
89 BEGET_LOGE("error 0x%x, sha256 init", ret);
90 return -1;
91 }
92
93 for (n = 0; n < num_certs; n++) {
94 ret = hash_calc_update(&ctx, certs[n].data.addr, certs[n].data.size);
95 if (ret != HASH_OK) {
96 BEGET_LOGE("error 0x%x, sha256 update", ret);
97 return -1;
98 }
99 }
100
101 ret = hash_calc_do_final(&ctx, NULL, 0, (uint8_t *)digest, size);
102 if (ret != HASH_OK) {
103 BEGET_LOGE("error 0x%x, sha256 final", ret);
104 return -1;
105 }
106
107 return 0;
108 }
109
FsHvbHexStrToBin(char * bin,size_t bin_size,char * str,size_t str_size)110 static int FsHvbHexStrToBin(char *bin, size_t bin_size, char *str, size_t str_size)
111 {
112 size_t i;
113
114 if ((str_size & 0x1) != 0 || bin_size < (str_size >> 1)) {
115 BEGET_LOGE("error, bin_size %zu str_size %zu", bin_size, str_size);
116 return -1;
117 }
118
119 for (i = 0; i < (str_size >> 1); i++) {
120 bin[i] = (FsHvbHexCharToBin(str[2 * i]) << 4) | FsHvbHexCharToBin(str[2 * i + 1]);
121 }
122
123 return 0;
124 }
125
FsHvbCheckCertChainDigest(struct hvb_cert_data * certs,uint64_t num_certs)126 static int FsHvbCheckCertChainDigest(struct hvb_cert_data *certs, uint64_t num_certs)
127 {
128 int ret;
129 size_t digest_len = 0;
130 char hashAlg[FS_HVB_HASH_ALG_STR_MAX] = {0};
131 char certDigest[FS_HVB_DIGEST_MAX_BYTES] = {0};
132 char computeDigest[FS_HVB_DIGEST_MAX_BYTES] = {0};
133 char certDigestStr[FS_HVB_DIGEST_STR_MAX_BYTES + 1] = {0};
134
135 ret = FsHvbGetHashStr(&hashAlg[0], sizeof(hashAlg));
136 if (ret != 0) {
137 BEGET_LOGE("error 0x%x, get hash val from cmdline", ret);
138 return ret;
139 }
140
141 ret = FsHvbGetCertDigstStr(&certDigestStr[0], sizeof(certDigestStr));
142 if (ret != 0) {
143 BEGET_LOGE("error 0x%x, get digest val from cmdline", ret);
144 return ret;
145 }
146
147 ret = FsHvbHexStrToBin(&certDigest[0], sizeof(certDigest), &certDigestStr[0], strlen(certDigestStr));
148 if (ret != 0) {
149 return ret;
150 }
151
152 if (strcmp(&hashAlg[0], "sha256") == 0) {
153 digest_len = FS_HVB_DIGEST_SHA256_BYTES;
154 ret = FsHvbComputeSha256(computeDigest, sizeof(computeDigest), certs, num_certs);
155 } else {
156 BEGET_LOGE("error, not support alg %s", &hashAlg[0]);
157 return -1;
158 }
159
160 if (ret != 0) {
161 BEGET_LOGE("error 0x%x, compute hash", ret);
162 return -1;
163 }
164
165 ret = memcmp(&certDigest[0], &computeDigest[0], digest_len);
166 if (ret != 0) {
167 BEGET_LOGE("error, cert digest not match with cmdline");
168 return -1;
169 }
170
171 return 0;
172 }
173
FsHvbInit(void)174 int FsHvbInit(void)
175 {
176 enum hvb_errno rc;
177
178 // return ok if the hvb is already initialized
179 if (g_vd != NULL) {
180 BEGET_LOGI("Hvb has already been inited");
181 return 0;
182 }
183
184 rc = hvb_chain_verify(FsHvbGetOps(), FS_HVB_RVT_PARTITION_NAME, NULL, &g_vd);
185 if (g_vd == NULL) {
186 BEGET_LOGE("error 0x%x, hvb chain verify, vd is NULL", rc);
187 return rc;
188 }
189
190 if (rc != HVB_OK) {
191 BEGET_LOGE("error 0x%x, hvb chain verify", rc);
192 hvb_chain_verify_data_free(g_vd);
193 g_vd = NULL;
194 return rc;
195 }
196
197 rc = FsHvbCheckCertChainDigest(g_vd->certs, g_vd->num_loaded_certs);
198 if (rc != 0) {
199 BEGET_LOGE("error 0x%x, cert chain hash", rc);
200 hvb_chain_verify_data_free(g_vd);
201 g_vd = NULL;
202 return rc;
203 }
204
205 return 0;
206 }
207
FsHvbGetCert(struct hvb_cert * cert,char * devName,struct hvb_verified_data * vd)208 static int FsHvbGetCert(struct hvb_cert *cert, char *devName, struct hvb_verified_data *vd)
209 {
210 enum hvb_errno hr;
211 size_t devNameLen = strlen(devName);
212 struct hvb_cert_data *p = vd->certs;
213 struct hvb_cert_data *end = p + vd->num_loaded_certs;
214
215 for (; p < end; p++) {
216 if (devNameLen != strlen(p->partition_name)) {
217 continue;
218 }
219
220 if (memcmp(p->partition_name, devName, devNameLen) == 0) {
221 break;
222 }
223 }
224
225 if (p == end) {
226 BEGET_LOGE("error, can't found %s partition", devName);
227 return -1;
228 }
229
230 hr = hvb_cert_parser(cert, &p->data);
231 if (hr != HVB_OK) {
232 BEGET_LOGE("error 0x%x, parser hvb cert", hr);
233 return -1;
234 }
235
236 return 0;
237 }
238
FsHvbVerityTargetAppendString(char ** p,char * end,char * str,size_t len)239 static int FsHvbVerityTargetAppendString(char **p, char *end, char *str, size_t len)
240 {
241 errno_t err;
242
243 // check range for append string
244 if (*p + len >= end || *p + len < *p) {
245 BEGET_LOGE("error, append string overflow");
246 return -1;
247 }
248 // append string
249 err = memcpy_s(*p, end - *p, str, len);
250 if (err != EOK) {
251 BEGET_LOGE("error 0x%x, cp string fail", err);
252 return -1;
253 }
254 *p += len;
255
256 // check range for append blank space
257 if (*p + 1 >= end || *p + 1 < *p) {
258 BEGET_LOGE("error, append blank space overflow");
259 return -1;
260 }
261 // append blank space
262 **p = FS_HVB_BLANK_SPACE_ASCII;
263 *p += 1;
264
265 BEGET_LOGE("append string %s", str);
266
267 return 0;
268 }
269
FsHvbVerityTargetAppendOctets(char ** p,char * end,char * octs,size_t octs_len)270 static int FsHvbVerityTargetAppendOctets(char **p, char *end, char *octs, size_t octs_len)
271 {
272 size_t i;
273 int rc;
274 char *str = NULL;
275 size_t str_len = octs_len * 2;
276
277 str = calloc(1, str_len + 1);
278 if (str == NULL) {
279 BEGET_LOGE("error, calloc str fail");
280 return -1;
281 }
282
283 for (i = 0; i < octs_len; i++) {
284 str[2 * i] = FsHvbBinToHexChar((octs[i] >> 4) & 0xf);
285 str[2 * i + 1] = FsHvbBinToHexChar(octs[i] & 0xf);
286 }
287
288 rc = FsHvbVerityTargetAppendString(p, end, str, str_len);
289 if (rc != 0) {
290 BEGET_LOGE("error 0x%x, append str fail", rc);
291 }
292 free(str);
293
294 return rc;
295 }
296
FsHvbVerityTargetAppendNum(char ** p,char * end,uint64_t num)297 static int FsHvbVerityTargetAppendNum(char **p, char *end, uint64_t num)
298 {
299 int rc;
300 char num_str[FS_HVB_UINT64_MAX_LENGTH] = {0};
301
302 rc = sprintf_s(&num_str[0], sizeof(num_str), "%llu", num);
303 if (rc < 0) {
304 BEGET_LOGE("error 0x%x, calloc num_str", rc);
305 return rc;
306 }
307
308 rc = FsHvbVerityTargetAppendString(p, end, num_str, strlen(&num_str[0]));
309 if (rc != 0) {
310 BEGET_LOGE("error 0x%x, append num_str fail", rc);
311 }
312
313 return rc;
314 }
315
316 #define RETURN_ERR_IF_APPEND_STRING_ERR(p, end, str, str_len) \
317 do { \
318 int __ret = FsHvbVerityTargetAppendString(p, end, str, str_len); \
319 if (__ret != 0) \
320 return __ret; \
321 } while (0)
322
323 #define RETURN_ERR_IF_APPEND_OCTETS_ERR(p, end, octs, octs_len) \
324 do { \
325 int __ret = FsHvbVerityTargetAppendOctets(p, end, octs, octs_len); \
326 if (__ret != 0) \
327 return __ret; \
328 } while (0)
329
330 #define RETURN_ERR_IF_APPEND_DIGIT_ERR(p, end, num) \
331 do { \
332 int __ret = FsHvbVerityTargetAppendNum(p, end, num); \
333 if (__ret != 0) \
334 return __ret; \
335 } while (0)
336
FsHvbGetHashAlgStr(unsigned int hash_algo)337 static char *FsHvbGetHashAlgStr(unsigned int hash_algo)
338 {
339 char *alg = NULL;
340
341 switch (hash_algo) {
342 case 0:
343 alg = "sha256";
344 break;
345
346 case 1:
347 alg = "sha512";
348 break;
349
350 default:
351 alg = NULL;
352 break;
353 }
354
355 return alg;
356 }
357
358 /*
359 * target->paras is verity table target, format as below;
360 * <version> <dev><hash_dev><data_block_size><hash_block_size>
361 * <num_data_blocks><hash_start_block><algorithm><digest><salt>
362 *[<#opt_params><opt_params>]
363 *exp: 1 /dev/sda1 /dev/sda1 4096 4096 262144 262144 sha256 \
364 xxxxx
365 xxxxx
366 */
367
FsHvbConstructVerityTarget(DmVerityTarget * target,const char * devName,struct hvb_cert * cert)368 int FsHvbConstructVerityTarget(DmVerityTarget *target, const char *devName, struct hvb_cert *cert)
369 {
370 char *p = NULL;
371 char *end = NULL;
372 char *hashALgo = NULL;
373 char devPath[FS_HVB_DEVPATH_MAX_LEN] = {0};
374
375 target->start = 0;
376 target->length = cert->image_len / FS_HVB_SECTOR_BYTES;
377 target->paras = calloc(1, FS_HVB_VERITY_TARGET_MAX); // simple it, just calloc a big mem
378 if (target->paras == NULL) {
379 BEGET_LOGE("error, alloc target paras");
380 return -1;
381 }
382
383 if (snprintf_s(&devPath[0], sizeof(devPath), sizeof(devPath) - 1, "%s%s",
384 ((strchr(devName, '/') == NULL) ? FS_HVB_PARTITION_PREFIX : ""), devName) == -1) {
385 BEGET_LOGE("error, snprintf_s devPath");
386 return -1;
387 }
388
389 BEGET_LOGE("puck devPath=%s", &devPath[0]);
390 p = target->paras;
391 end = p + FS_HVB_VERITY_TARGET_MAX;
392
393 // append <version>
394 RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, 1);
395 // append <dev>
396 RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, &devPath[0], strlen(devPath));
397 // append <hash_dev>
398 RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, &devPath[0], strlen(devPath));
399 // append <data_block_size>
400 RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->data_block_size);
401 // append <hash_block_size>
402 RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->hash_block_size);
403 // append <num_data_blocks>
404 RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->image_len / cert->data_block_size);
405 // append <hash_start_block>
406 RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->hashtree_offset / cert->hash_block_size);
407
408 // append <algorithm>
409 hashALgo = FsHvbGetHashAlgStr(cert->hash_algo);
410 if (hashALgo == NULL) {
411 BEGET_LOGE("error, hash alg %d is invalid", cert->hash_algo);
412 return -1;
413 }
414 RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, hashALgo, strlen(hashALgo));
415
416 // append <digest>
417 RETURN_ERR_IF_APPEND_OCTETS_ERR(&p, end, (char *)cert->hash_payload.digest, cert->digest_size);
418
419 // append <salt>
420 RETURN_ERR_IF_APPEND_OCTETS_ERR(&p, end, (char *)cert->hash_payload.salt, cert->salt_size);
421
422 //remove last blank
423 *(p - 1) = '\0';
424
425 target->paras_len = strlen(target->paras);
426
427 return 0;
428 }
429
FsHvbCreateVerityTarget(DmVerityTarget * target,char * devName,struct hvb_verified_data * vd)430 static int FsHvbCreateVerityTarget(DmVerityTarget *target, char *devName, struct hvb_verified_data *vd)
431 {
432 int rc;
433 struct hvb_cert cert = {0};
434
435 rc = FsHvbGetCert(&cert, devName, vd);
436 if (rc != 0) {
437 return rc;
438 }
439
440 rc = FsHvbConstructVerityTarget(target, devName, &cert);
441 if (rc != 0) {
442 BEGET_LOGE("error 0x%x, can't construct verity target", rc);
443 return rc;
444 }
445
446 return rc;
447 }
448
FsHvbDestoryVerityTarget(DmVerityTarget * target)449 void FsHvbDestoryVerityTarget(DmVerityTarget *target)
450 {
451 if (target != NULL && target->paras != NULL) {
452 free(target->paras);
453 target->paras = NULL;
454 }
455 }
456
FsHvbSetupHashtree(FstabItem * fsItem)457 int FsHvbSetupHashtree(FstabItem *fsItem)
458 {
459 int rc;
460 DmVerityTarget target = {0};
461 char *dmDevPath = NULL;
462 char *devName = NULL;
463
464 FS_HVB_RETURN_ERR_IF_NULL(fsItem);
465 FS_HVB_RETURN_ERR_IF_NULL(g_vd);
466
467 // fsItem->deviceName is like /dev/block/platform/xxx/by-name/system
468 // we just basename system
469 devName = basename(fsItem->deviceName);
470 if (devName == NULL) {
471 BEGET_LOGE("error, get basename");
472 return -1;
473 }
474
475 rc = FsHvbCreateVerityTarget(&target, devName, g_vd);
476 if (rc != 0) {
477 BEGET_LOGE("error 0x%x, create verity-table", rc);
478 goto exit;
479 }
480
481 rc = FsDmCreateDevice(&dmDevPath, devName, &target);
482 if (rc != 0) {
483 BEGET_LOGE("error 0x%x, create dm-verity", rc);
484 goto exit;
485 }
486
487 rc = FsDmInitDmDev(dmDevPath, true);
488 if (rc != 0) {
489 BEGET_LOGE("error 0x%x, create init dm dev", rc);
490 goto exit;
491 }
492
493 free(fsItem->deviceName);
494 fsItem->deviceName = dmDevPath;
495
496 exit:
497 FsHvbDestoryVerityTarget(&target);
498
499 return rc;
500 }
501
FsHvbFinal(void)502 int FsHvbFinal(void)
503 {
504 if (g_vd != NULL) {
505 hvb_chain_verify_data_free(g_vd);
506 }
507
508 return 0;
509 }
510
FsHvbGetValueFromCmdLine(char * val,size_t size,const char * key)511 int FsHvbGetValueFromCmdLine(char *val, size_t size, const char *key)
512 {
513 FS_HVB_RETURN_ERR_IF_NULL(val);
514 FS_HVB_RETURN_ERR_IF_NULL(key);
515
516 return GetParameterFromCmdLine(key, val, size);
517 }
518
519 #ifdef __cplusplus
520 #if __cplusplus
521 }
522 #endif
523 #endif
524