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#include "hvb_cmdline.h"
16#include "hvb_util.h"
17#include "hvb_cert.h"
18#include "hvb_rvt.h"
19#include "hvb_ops.h"
20#include "hvb.h"
21#include "hvb_sysdeps.h"
22#include "hvb_crypto.h"
23
24static int cmdline_append_option(struct hvb_verified_data *vd, const char *key, const char *value)
25{
26    uint64_t option_len = 0;
27    uint64_t key_len, value_len;
28    struct hvb_cmdline_data *cmdline = NULL;
29
30    if (vd == NULL || vd->cmdline.buf == NULL)
31        return 0;
32
33    cmdline = &vd->cmdline;
34
35    key_len = hvb_strlen(key);
36    value_len = hvb_strlen(value);
37    /* 2 for blank space and = */
38    option_len = key_len + value_len + 2;
39    if (option_len > cmdline->max_size - cmdline->cur_pos - 1)
40        return 0;
41
42    /* append blank space */
43    cmdline->buf[cmdline->cur_pos] = ' ';
44    cmdline->cur_pos++;
45    /* append key */
46    hvb_memcpy(cmdline->buf + cmdline->cur_pos, key, key_len);
47    cmdline->cur_pos += key_len;
48    /* append = */
49    cmdline->buf[cmdline->cur_pos] = '=';
50    cmdline->cur_pos++;
51    /* append value */
52    hvb_memcpy(cmdline->buf + cmdline->cur_pos, value, value_len);
53    cmdline->cur_pos += value_len;
54
55    return 1;
56}
57
58static int hvb_append_version_cmdline(struct hvb_verified_data *vd, const char *key_value,
59                                      uint64_t version_major, uint64_t version_minor)
60{
61    char major_digits[HVB_MAX_DIGITS_UINT64];
62    char minor_digits[HVB_MAX_DIGITS_UINT64];
63    char combined[HVB_MAX_DIGITS_UINT64 * 2 + 1];
64    uint64_t num_major_digits, num_minor_digits;
65
66    num_major_digits = hvb_uint64_to_base10(version_major, major_digits);
67    num_minor_digits = hvb_uint64_to_base10(version_minor, minor_digits);
68
69    hvb_memcpy(combined, major_digits, num_major_digits);
70    combined[num_major_digits] = '.';
71    hvb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
72    combined[num_major_digits + 1 + num_minor_digits] = '\0';
73
74    return cmdline_append_option(vd, key_value, combined);
75}
76
77static int cmdline_append_uint64_base10(struct hvb_verified_data *vd, const char *key, uint64_t value)
78{
79    char digits[HVB_MAX_DIGITS_UINT64];
80
81    hvb_uint64_to_base10(value, digits);
82
83    return cmdline_append_option(vd, key, digits);
84}
85
86static int cmdline_append_hex(struct hvb_verified_data *vd, const char* key,
87                              const uint8_t *data, uint64_t data_len)
88{
89    int ret;
90    char *hex_data = hvb_bin2hex(data, data_len);
91
92    if (hex_data == NULL) {
93        return 0;
94    }
95
96    ret = cmdline_append_option(vd, key, hex_data);
97    hvb_free(hex_data);
98
99    return ret;
100}
101
102enum hvb_errno hvb_creat_cmdline(struct hvb_ops *ops, struct hvb_verified_data *vd)
103{
104    hvb_return_hvb_err_if_null(ops);
105    hvb_return_hvb_err_if_null(vd);
106
107    enum hvb_errno ret = HVB_OK;
108    enum hvb_io_errno io_ret = HVB_IO_OK;
109    bool device_locked = false;
110    ret = check_hvb_ops(ops);
111    if (ret != HVB_OK) {
112        hvb_print("error, check ops\n");
113        return HVB_ERROR_INVALID_ARGUMENT;
114    }
115
116    /* set ohos.boot.hvb.version. */
117    if (!hvb_append_version_cmdline(vd, HVB_CMDLINE_VERSION,
118                                    HVB_VERSION_MAJOR, HVB_VERSION_MINOR)) {
119        ret = HVB_ERROR_OOM;
120        goto fail;
121    }
122
123    /* set ohos.boot.device_state to "locked" or "unlocked". */
124    io_ret = ops->read_lock_state(ops, &device_locked);
125    if (io_ret == HVB_IO_ERROR_OOM) {
126        ret = HVB_ERROR_OOM;
127        goto fail;
128    } else if (io_ret != HVB_IO_OK) {
129        hvb_print("Error getting device state.\n");
130        ret = HVB_ERROR_IO;
131        goto fail;
132    }
133
134    if (!cmdline_append_option(vd, HVB_CMDLINE_DEV_STATE,
135                               device_locked ? "locked" : "unlocked")) {
136        ret = HVB_ERROR_OOM;
137        goto fail;
138    }
139
140    /*
141     * set ohos.boot.hvb.{hash_algo, size, digest} - use same hash
142     * function as is used to sign rvt.
143     */
144    uint8_t rvt_digest[HVB_SHA256_DIGEST_BYTES] = {0};
145    uint64_t rvt_size = 0;
146    for (uint64_t n = 0; n < vd->num_loaded_certs; n++) {
147        rvt_size += vd->certs[n].data.size;
148    }
149
150    if (hvb_calculate_certs_digest(vd, rvt_digest) != HVB_OK) {
151        hvb_print("Error calculate rvt digest.\n");
152        ret = HVB_ERROR_OOM;
153        goto fail;
154    }
155
156    if (!cmdline_append_option(vd, HVB_CMDLINE_HASH_ALG, "sha256") ||
157        !cmdline_append_uint64_base10(vd, HVB_CMDLINE_RVT_SIZE, rvt_size) ||
158        !cmdline_append_hex(vd, HVB_CMDLINE_CERT_DIGEST, rvt_digest, HVB_SHA256_DIGEST_BYTES)) {
159        ret = HVB_ERROR_OOM;
160        goto fail;
161    }
162
163fail:
164    return ret;
165}
166