• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         return ret;
138     }
139 
140     ret = FsHvbGetCertDigstStr(&certDigestStr[0], sizeof(certDigestStr));
141     if (ret != 0) {
142         return ret;
143     }
144 
145     ret = FsHvbHexStrToBin(&certDigest[0], sizeof(certDigest), &certDigestStr[0], strlen(certDigestStr));
146     if (ret != 0) {
147         return ret;
148     }
149 
150     if (strcmp(&hashAlg[0], "sha256") == 0) {
151         digest_len = FS_HVB_DIGEST_SHA256_BYTES;
152         ret = FsHvbComputeSha256(computeDigest, sizeof(computeDigest), certs, num_certs);
153     } else {
154         BEGET_LOGE("error, not support alg %s", &hashAlg[0]);
155         return -1;
156     }
157 
158     if (ret != 0) {
159         BEGET_LOGE("error 0x%x, compute hash", ret);
160         return -1;
161     }
162 
163     ret = memcmp(&certDigest[0], &computeDigest[0], digest_len);
164     if (ret != 0) {
165         BEGET_LOGE("error, cert digest not match with cmdline");
166         return -1;
167     }
168 
169     return 0;
170 }
171 
FsHvbInit(void)172 int FsHvbInit(void)
173 {
174     enum hvb_errno rc;
175 
176     // return ok if the hvb is already initialized
177     if (g_vd != NULL) {
178         BEGET_LOGI("Hvb has already been inited");
179         return 0;
180     }
181 
182     rc = hvb_chain_verify(FsHvbGetOps(), FS_HVB_RVT_PARTITION_NAME, NULL, &g_vd);
183     if (g_vd == NULL) {
184         BEGET_LOGE("error 0x%x, hvb chain verify, vd is NULL", rc);
185         return rc;
186     }
187 
188     if (rc != HVB_OK) {
189         BEGET_LOGE("error 0x%x, hvb chain verify", rc);
190         hvb_chain_verify_data_free(g_vd);
191         g_vd = NULL;
192         return rc;
193     }
194 
195     rc = FsHvbCheckCertChainDigest(g_vd->certs, g_vd->num_loaded_certs);
196     if (rc != 0) {
197         BEGET_LOGE("error 0x%x, cert chain hash", rc);
198         hvb_chain_verify_data_free(g_vd);
199         g_vd = NULL;
200         return rc;
201     }
202 
203     return 0;
204 }
205 
FsHvbGetCert(struct hvb_cert * cert,char * devName,struct hvb_verified_data * vd)206 static int FsHvbGetCert(struct hvb_cert *cert, char *devName, struct hvb_verified_data *vd)
207 {
208     enum hvb_errno hr;
209     size_t devNameLen = strlen(devName);
210     struct hvb_cert_data *p = vd->certs;
211     struct hvb_cert_data *end = p + vd->num_loaded_certs;
212 
213     for (; p < end; p++) {
214         if (devNameLen != strlen(p->partition_name)) {
215             continue;
216         }
217 
218         if (memcmp(p->partition_name, devName, devNameLen) == 0) {
219             break;
220         }
221     }
222 
223     if (p == end) {
224         BEGET_LOGE("error, can't found %s partition", devName);
225         return -1;
226     }
227 
228     hr = hvb_cert_parser(cert, &p->data);
229     if (hr != HVB_OK) {
230         BEGET_LOGE("error 0x%x, parser hvb cert", hr);
231         return -1;
232     }
233 
234     return 0;
235 }
236 
FsHvbVerityTargetAppendString(char ** p,char * end,char * str,size_t len)237 static int FsHvbVerityTargetAppendString(char **p, char *end, char *str, size_t len)
238 {
239     errno_t err;
240 
241     // check range for append string
242     if (*p + len >= end || *p + len < *p) {
243         BEGET_LOGE("error, append string overflow");
244         return -1;
245     }
246     // append string
247     err = memcpy_s(*p, end - *p, str, len);
248     if (err != EOK) {
249         BEGET_LOGE("error 0x%x, cp string fail", err);
250         return -1;
251     }
252     *p += len;
253 
254     // check range for append blank space
255     if (*p + 1 >= end || *p + 1 < *p) {
256         BEGET_LOGE("error, append blank space overflow");
257         return -1;
258     }
259     // append blank space
260     **p = FS_HVB_BLANK_SPACE_ASCII;
261     *p += 1;
262 
263     BEGET_LOGE("append string %s", str);
264 
265     return 0;
266 }
267 
FsHvbVerityTargetAppendOctets(char ** p,char * end,char * octs,size_t octs_len)268 static int FsHvbVerityTargetAppendOctets(char **p, char *end, char *octs, size_t octs_len)
269 {
270     size_t i;
271     int rc;
272     char *str = NULL;
273     size_t str_len = octs_len * 2;
274 
275     str = calloc(1, str_len + 1);
276     if (str == NULL) {
277         BEGET_LOGE("error, calloc str fail");
278         return -1;
279     }
280 
281     for (i = 0; i < octs_len; i++) {
282         str[2 * i] = FsHvbBinToHexChar((octs[i] >> 4) & 0xf);
283         str[2 * i + 1] = FsHvbBinToHexChar(octs[i] & 0xf);
284     }
285 
286     rc = FsHvbVerityTargetAppendString(p, end, str, str_len);
287     if (rc != 0) {
288         BEGET_LOGE("error 0x%x, append str fail", rc);
289     }
290     free(str);
291 
292     return rc;
293 }
294 
FsHvbVerityTargetAppendNum(char ** p,char * end,uint64_t num)295 static int FsHvbVerityTargetAppendNum(char **p, char *end, uint64_t num)
296 {
297     int rc;
298     char num_str[FS_HVB_UINT64_MAX_LENGTH] = {0};
299 
300     rc = sprintf_s(&num_str[0], sizeof(num_str), "%llu", num);
301     if (rc < 0) {
302         BEGET_LOGE("error 0x%x, calloc num_str", rc);
303         return rc;
304     }
305 
306     rc = FsHvbVerityTargetAppendString(p, end, num_str, strlen(&num_str[0]));
307     if (rc != 0) {
308         BEGET_LOGE("error 0x%x, append num_str fail", rc);
309     }
310 
311     return rc;
312 }
313 
314 #define RETURN_ERR_IF_APPEND_STRING_ERR(p, end, str, str_len)                     \
315     do {                                                                          \
316         int __ret = FsHvbVerityTargetAppendString(p, end, str, str_len);          \
317         if (__ret != 0)                                                           \
318             return __ret;                                                         \
319     } while (0)
320 
321 #define RETURN_ERR_IF_APPEND_OCTETS_ERR(p, end, octs, octs_len)                    \
322     do {                                                                          \
323         int __ret = FsHvbVerityTargetAppendOctets(p, end, octs, octs_len);          \
324         if (__ret != 0)                                                           \
325             return __ret;                                                         \
326     } while (0)
327 
328 #define RETURN_ERR_IF_APPEND_DIGIT_ERR(p, end, num)                               \
329     do {                                                                          \
330         int __ret = FsHvbVerityTargetAppendNum(p, end, num);                      \
331         if (__ret != 0)                                                           \
332             return __ret;                                                         \
333     } while (0)
334 
FsHvbGetHashAlgStr(unsigned int hash_algo)335 static char *FsHvbGetHashAlgStr(unsigned int hash_algo)
336 {
337     char *alg = NULL;
338 
339     switch (hash_algo) {
340         case 0:
341             alg = "sha256";
342             break;
343 
344         case 1:
345             alg = "sha512";
346             break;
347 
348         default:
349             alg = NULL;
350             break;
351     }
352 
353     return alg;
354 }
355 
356 /*
357  * target->paras is verity table target, format as below;
358  * <version> <dev><hash_dev><data_block_size><hash_block_size>
359  * <num_data_blocks><hash_start_block><algorithm><digest><salt>
360  *[<#opt_params><opt_params>]
361  *exp: 1 /dev/sda1 /dev/sda1 4096 4096 262144 262144 sha256 \
362        xxxxx
363        xxxxx
364  */
365 
FsHvbConstructVerityTarget(DmVerityTarget * target,const char * devName,struct hvb_cert * cert)366 int FsHvbConstructVerityTarget(DmVerityTarget *target, const char *devName, struct hvb_cert *cert)
367 {
368     char *p = NULL;
369     char *end = NULL;
370     char *hashALgo = NULL;
371     char devPath[FS_HVB_DEVPATH_MAX_LEN] = {0};
372 
373     target->start = 0;
374     target->length = cert->image_len / FS_HVB_SECTOR_BYTES;
375     target->paras = calloc(1, FS_HVB_VERITY_TARGET_MAX); // simple it, just calloc a big mem
376     if (target->paras == NULL) {
377         BEGET_LOGE("error, alloc target paras");
378         return -1;
379     }
380 
381     if (snprintf_s(&devPath[0], sizeof(devPath), sizeof(devPath) - 1, "%s%s",
382         ((strchr(devName, '/') == NULL) ? FS_HVB_PARTITION_PREFIX : ""), devName) == -1) {
383         BEGET_LOGE("error, snprintf_s devPath");
384         return -1;
385     }
386 
387     BEGET_LOGE("puck devPath=%s", &devPath[0]);
388     p = target->paras;
389     end = p + FS_HVB_VERITY_TARGET_MAX;
390 
391     // append <version>
392     RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, 1);
393     // append <dev>
394     RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, &devPath[0], strlen(devPath));
395     // append <hash_dev>
396     RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, &devPath[0], strlen(devPath));
397     // append <data_block_size>
398     RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->data_block_size);
399     // append <hash_block_size>
400     RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->hash_block_size);
401     // append <num_data_blocks>
402     RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->image_len / cert->data_block_size);
403     // append <hash_start_block>
404     RETURN_ERR_IF_APPEND_DIGIT_ERR(&p, end, cert->hashtree_offset / cert->hash_block_size);
405 
406     // append <algorithm>
407     hashALgo = FsHvbGetHashAlgStr(cert->hash_algo);
408     if (hashALgo == NULL) {
409         BEGET_LOGE("error, hash alg %d is invalid", cert->hash_algo);
410         return -1;
411     }
412     RETURN_ERR_IF_APPEND_STRING_ERR(&p, end, hashALgo, strlen(hashALgo));
413 
414     // append <digest>
415     RETURN_ERR_IF_APPEND_OCTETS_ERR(&p, end, (char *)cert->hash_payload.digest, cert->digest_size);
416 
417     // append <salt>
418     RETURN_ERR_IF_APPEND_OCTETS_ERR(&p, end, (char *)cert->hash_payload.salt, cert->salt_size);
419 
420     //remove last blank
421     *(p - 1) = '\0';
422 
423     target->paras_len = strlen(target->paras);
424 
425     return 0;
426 }
427 
FsHvbCreateVerityTarget(DmVerityTarget * target,char * devName,struct hvb_verified_data * vd)428 static int FsHvbCreateVerityTarget(DmVerityTarget *target, char *devName, struct hvb_verified_data *vd)
429 {
430     int rc;
431     struct hvb_cert cert = {0};
432 
433     rc = FsHvbGetCert(&cert, devName, vd);
434     if (rc != 0) {
435         return rc;
436     }
437 
438     rc = FsHvbConstructVerityTarget(target, devName, &cert);
439     if (rc != 0) {
440         BEGET_LOGE("error 0x%x, can't construct verity target", rc);
441         return rc;
442     }
443 
444     return rc;
445 }
446 
FsHvbDestoryVerityTarget(DmVerityTarget * target)447 void FsHvbDestoryVerityTarget(DmVerityTarget *target)
448 {
449     if (target != NULL && target->paras != NULL) {
450         free(target->paras);
451         target->paras = NULL;
452     }
453 }
454 
FsHvbSetupHashtree(FstabItem * fsItem)455 int FsHvbSetupHashtree(FstabItem *fsItem)
456 {
457     int rc;
458     DmVerityTarget target = {0};
459     char *dmDevPath = NULL;
460     char *devName = NULL;
461 
462     FS_HVB_RETURN_ERR_IF_NULL(fsItem);
463     FS_HVB_RETURN_ERR_IF_NULL(g_vd);
464 
465     // fsItem->deviceName is like /dev/block/platform/xxx/by-name/system
466     // we just basename system
467     devName = basename(fsItem->deviceName);
468     if (devName == NULL) {
469         BEGET_LOGE("error, get basename");
470         return -1;
471     }
472 
473     rc = FsHvbCreateVerityTarget(&target, devName, g_vd);
474     if (rc != 0) {
475         BEGET_LOGE("error 0x%x, create verity-table", rc);
476         goto exit;
477     }
478 
479     rc = FsDmCreateDevice(&dmDevPath, devName, &target);
480     if (rc != 0) {
481         BEGET_LOGE("error 0x%x, create dm-verity", rc);
482         goto exit;
483     }
484 
485     rc = FsDmInitDmDev(dmDevPath);
486     if (rc != 0) {
487         BEGET_LOGE("error 0x%x, create init dm dev", rc);
488         goto exit;
489     }
490 
491     free(fsItem->deviceName);
492     fsItem->deviceName = dmDevPath;
493 
494 exit:
495     FsHvbDestoryVerityTarget(&target);
496 
497     return rc;
498 }
499 
FsHvbFinal(void)500 int FsHvbFinal(void)
501 {
502     if (g_vd != NULL) {
503         hvb_chain_verify_data_free(g_vd);
504     }
505 
506     return 0;
507 }
508 
FsHvbGetValueFromCmdLine(char * val,size_t size,const char * key)509 int FsHvbGetValueFromCmdLine(char *val, size_t size, const char *key)
510 {
511     int ret;
512 
513     FS_HVB_RETURN_ERR_IF_NULL(val);
514     FS_HVB_RETURN_ERR_IF_NULL(key);
515     ret = GetParameterFromCmdLine(key, val, size);
516     if (ret != 0) {
517         BEGET_LOGE("error 0x%x, get %s val from cmdline", ret, key);
518     }
519     return ret;
520 }
521 
522 #ifdef __cplusplus
523 #if __cplusplus
524 }
525 #endif
526 #endif
527