1/*
2 * Copyright (c) 2024-2024 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 "cmd_util.h"
16#include <set>
17#include <filesystem>
18
19#include "params_run_tool.h"
20#include "constant.h"
21#include "param_constants.h"
22
23namespace OHOS {
24namespace SignatureTools {
25const std::regex INTEGER_PATTERN = std::regex("\\d{1,10}");
26
27bool CmdUtil::String2Bool(Options* options, const std::string& option)
28{
29    std::string val = options->GetString(option);
30    if (val == "1" || val == "true" || val == "TRUE") {
31        (*options)[option] = true;
32    } else if (val == "0" || val == "false" || val == "FALSE") {
33        (*options)[option] = false;
34    } else {
35        PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
36                            val + "is not valid value for " + "-" + option);
37        return false;
38    }
39    return true;
40}
41
42static bool UpdateParamForVariantCertInt(const ParamsSharedPtr& param)
43{
44    int defaultValidity = 0;
45    Options* options = param->GetOptions();
46    if (options->count(Options::VALIDITY)) {
47        int validity = 0;
48        std::string val = options->GetString(Options::VALIDITY);
49        for (char x : val) {
50            if (!isdigit(x)) {
51                PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "Invalid parameter '"
52                                    + val + "', You should fill in the numbers");
53                return false;
54            }
55        }
56        if (!StringUtils::CheckStringToint(val, validity)) {
57            PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "Invalid parameter '"
58                                + val + "'");
59            return false;
60        }
61        validity *= ONE_DAY_TIME;
62        (*options)[Options::VALIDITY] = validity;
63    } else if (param->GetMethod() == GENERATE_CA || param->GetMethod() == GENERATE_APP_CERT ||
64               param->GetMethod() == GENERATE_PROFILE_CERT) {
65        defaultValidity = DEFAULT_VALIDITY_DAYS * ONE_DAY_TIME;
66        (*options)[Options::VALIDITY] = defaultValidity;
67    } else if (param->GetMethod() == GENERATE_CERT) {
68        defaultValidity = DEFAULT_CUSTOM_VALIDITY_DAYS * ONE_DAY_TIME;
69        (*options)[Options::VALIDITY] = defaultValidity;
70    }
71    return true;
72}
73
74static bool UpdateParamForVariantInt(const ParamsSharedPtr& param)
75{
76    Options* options = param->GetOptions();
77    // general
78    if (options->count(Options::KEY_SIZE)) {
79        std::string keySize = options->GetString(Options::KEY_SIZE);
80        if (keySize == "NIST-P-256") {
81            (*options)[Options::KEY_SIZE] = NIST_P_256;
82        } else if (keySize == "NIST-P-384") {
83            (*options)[Options::KEY_SIZE] = NIST_P_384;
84        } else {
85            PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "not supported '" + keySize
86                                + "' Key algorithms length");
87            return false;
88        }
89    }
90    if (options->count(Options::BASIC_CONSTRAINTS_PATH_LEN)) {
91        int basicConstraintsPathLen = 0;
92        std::string val = options->GetString(Options::BASIC_CONSTRAINTS_PATH_LEN);
93        if (!StringUtils::CheckStringToint(val, basicConstraintsPathLen)) {
94            PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "Invalid parameter '"
95                                + val + "', You should fill in the numbers");
96            return false;
97        }
98        (*options)[Options::BASIC_CONSTRAINTS_PATH_LEN] = basicConstraintsPathLen;
99    } else if (param->GetMethod() == GENERATE_CA || param->GetMethod() == GENERATE_CERT) {
100        (*options)[Options::BASIC_CONSTRAINTS_PATH_LEN] = DEFAULT_BASIC_CONSTRAINTS_PATH_LEN;
101    }
102    if (!UpdateParamForVariantCertInt(param)) {
103        return false;
104    }
105    return true;
106}
107
108static bool UpdateParamForVariantBoolKeyUsage(const ParamsSharedPtr& param)
109{
110    Options* options = param->GetOptions();
111
112    //The bool type is used only by the "generate-cert" module
113    if (options->count(Options::KEY_USAGE_CRITICAL)) {
114        if (!CmdUtil::String2Bool(options, Options::KEY_USAGE_CRITICAL)) {
115            return false;
116        }
117    } else if (param->GetMethod() == GENERATE_CERT) {
118        (*options)[Options::KEY_USAGE_CRITICAL] = DEFAULT_KEY_USAGE_CRITICAL;
119    }
120
121    //The bool type is used only by the "generate-cert" module
122    if (options->count(Options::EXT_KEY_USAGE_CRITICAL)) {
123        if (!CmdUtil::String2Bool(options, Options::EXT_KEY_USAGE_CRITICAL)) {
124            return false;
125        }
126    } else if (param->GetMethod() == GENERATE_CERT) {
127        (*options)[Options::EXT_KEY_USAGE_CRITICAL] = DEFAULT_EXT_KEY_USAGE_CRITICAL;
128    }
129    return true;
130}
131
132static bool UpdateParamForVariantBoolProfileSigned(const ParamsSharedPtr& param)
133{
134    Options* options = param->GetOptions();
135
136    //The bool type is used only by the "sign-app" module
137    if (options->count(Options::PROFILE_SIGNED)) {
138        std::string val = options->GetString(Options::PROFILE_SIGNED);
139        if (val == "1" || val == "true" || val == "TRUE") {
140            (*options)[Options::PROFILE_SIGNED] = DEFAULT_PROFILE_SIGNED_1;
141        } else if (val == "0" || val == "false" || val == "FALSE") {
142            (*options)[Options::PROFILE_SIGNED] = DEFAULT_PROFILE_SIGNED_0;
143        } else {
144            PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
145                                val + "is not valid value for "+"-" + Options::PROFILE_SIGNED);
146            return false;
147        }
148    } else if (param->GetMethod() == SIGN_APP) {
149        (*options)[Options::PROFILE_SIGNED] = DEFAULT_PROFILE_SIGNED_1;
150    }
151
152    return true;
153}
154
155bool CmdUtil::UpdateParamForCheckOutFile(Options* options, const std::initializer_list<std::string>& outFileKeys)
156{
157    for (auto& key : outFileKeys) {
158        if (options->count(key)) {
159            std::string outFilePath = options->GetString(key);
160            std::filesystem::path filePath = outFilePath;
161            std::string parentPath = filePath.parent_path();
162
163            //Purpose: To prevent the user output path from passing an empty string. eg "   "
164            std::string tmpOutFilePath = outFilePath;
165            tmpOutFilePath.erase(std::remove_if(tmpOutFilePath.begin(),
166                tmpOutFilePath.end(), ::isspace), tmpOutFilePath.end());
167
168            if (parentPath.empty() && !tmpOutFilePath.empty()) {
169                parentPath = "./";
170            }
171            char realFilePath[PATH_MAX + 1] = {0x00};
172            if (parentPath.size() > PATH_MAX) {
173                PrintErrorNumberMsg("FILE_NOT_FOUND", FILE_NOT_FOUND, "'" + outFilePath + "' File path longer than '"
174                                    + std::to_string(PATH_MAX) + "' characters");
175                return false;
176            }
177            if (realpath(parentPath.c_str(), realFilePath) == nullptr) {
178                PrintErrorNumberMsg("FILE_NOT_FOUND", FILE_NOT_FOUND, "The '" + outFilePath +
179                                    "' file does not exist or the path is invalid"
180                                    + "', parameter name '-" + key + "'");
181                return false;
182            }
183            std::string charStr(realFilePath);
184            std::string fileName = filePath.filename();
185            if (fileName.empty()) {
186                PrintErrorNumberMsg("FILE_NOT_FOUND", FILE_NOT_FOUND, "The file name cannot be empty '"
187                                    + outFilePath + "', parameter name '-" + key + "'");
188                return false;
189            }
190            (*options)[key] = charStr + "/" + fileName;
191        }
192    }
193    return true;
194}
195
196bool CmdUtil::UpdateParamForCheckInFile(Options* options, const std::initializer_list<std::string>& inFileKeys)
197{
198    for (auto& key : inFileKeys) {
199        if (options->count(key)) {
200            std::string inFilePath = options->GetString(key);
201            char realFilePath[PATH_MAX + 1] = {0x00};
202            if (inFilePath.size() > PATH_MAX) {
203                PrintErrorNumberMsg("FILE_NOT_FOUND", FILE_NOT_FOUND, "'" + inFilePath + "' File path longer than '"
204                                    + std::to_string(PATH_MAX) + "' characters");
205                return false;
206            }
207            if (realpath(inFilePath.c_str(), realFilePath) == nullptr) {
208                PrintErrorNumberMsg("FILE_NOT_FOUND", FILE_NOT_FOUND, "The '" + inFilePath +
209                                    "' file does not exist or the path is invalid"
210                                    + "', parameter name '-" + key + "'");
211                return false;
212            }
213            std::string charStr(realFilePath);
214            (*options)[key] = charStr;
215
216            if (!FileUtils::IsValidFile(inFilePath)) {
217                return false;
218            }
219        }
220    }
221
222    return true;
223}
224
225static bool UpdateParamForCheckSignAlg(const ParamsSharedPtr& param)
226{
227    // check signAlg
228    Options* options = param->GetOptions();
229    if (options->count(Options::SIGN_ALG)) {
230        std::string signAlg = options->GetString(Options::SIGN_ALG);
231        if (signAlg != SIGN_ALG_SHA256 && signAlg != SIGN_ALG_SHA384) {
232            PrintErrorNumberMsg("NOT_SUPPORT_ERROR", NOT_SUPPORT_ERROR, "'" + signAlg + "' parameter is incorrect");
233            return false;
234        }
235    }
236    return true;
237}
238
239static bool UpdateParamForInform(const ParamsSharedPtr& param)
240{
241    // check sign_app verify_app inform
242    Options* options = param->GetOptions();
243    if (param->GetMethod() == SIGN_APP ||
244        param->GetMethod() == VERIFY_APP) {
245        if (options->count(Options::INFORM)) {
246            std::string inForm = options->GetString(Options::INFORM);
247            if (!StringUtils::ContainsCase(ParamsRunTool::InformList, inForm)) {
248                PrintErrorNumberMsg("NOT_SUPPORT_ERROR", NOT_SUPPORT_ERROR, "parameter '"
249                                    + inForm + "' format error, Inform only support zip/elf/bin");
250                return false;
251            }
252        } else {
253            (*options)[Options::INFORM] = ZIP;
254        }
255    }
256    return true;
257}
258
259static bool UpdateParamForOutform(const ParamsSharedPtr& param)
260{
261    // check generate_app_cert generate_profile_cert
262    Options* options = param->GetOptions();
263    if (param->GetMethod() == GENERATE_APP_CERT ||
264        param->GetMethod() == GENERATE_PROFILE_CERT) {
265        if (options->count(Options::OUT_FORM)) {
266            std::string outForm = options->GetString(Options::OUT_FORM);
267            if (outForm != OUT_FORM_CERT && outForm != OUT_FORM_CERT_CHAIN) {
268                PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "parameter '" + outForm
269                                    + "' format error, Outform only supprot cert/cerChain");
270                return false;
271            }
272        } else {
273            (*options)[Options::OUT_FORM] = OUT_FORM_CERT_CHAIN;
274        }
275    }
276    return true;
277}
278
279//Check "remoteSign" additional parameters are required
280static bool UpdateParamForCheckRemoteSignProfile(const ParamsSharedPtr& param)
281{
282    Options* options = param->GetOptions();
283    std::set<std::string> signProfileRemoteParams{ParamConstants::PARAM_REMOTE_SERVER,
284                                                ParamConstants::PARAM_REMOTE_USERNAME,
285                                                ParamConstants::PARAM_REMOTE_USERPWD,
286                                                ParamConstants::PARAM_REMOTE_ONLINEAUTHMODE,
287                                                ParamConstants::PARAM_REMOTE_SIGNERPLUGIN};
288
289    if (param->GetMethod() == SIGN_PROFILE && options->count(Options::MODE) &&
290        options->GetString(Options::MODE) == REMOTE_SIGN) {
291        for (const std::string& key : signProfileRemoteParams) {
292            if (options->count(key) == 0) {
293                PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "sign profile RemoteSign absence param '"
294                                    + key + "'");
295                return false;
296            }
297        }
298    }
299    return true;
300}
301
302static bool UpdateParam(const ParamsSharedPtr& param)
303{
304    if (!UpdateParamForVariantInt(param)) {
305        return false;
306    }
307    if (!UpdateParamForVariantBoolKeyUsage(param)) {
308        return false;
309    }
310    if (!UpdateParamForVariantBoolProfileSigned(param)) {
311        return false;
312    }
313    if (!UpdateParamForCheckSignAlg(param)) {
314        return false;
315    }
316    if (!UpdateParamForInform(param)) {
317        return false;
318    }
319    if (!UpdateParamForOutform(param)) {
320        return false;
321    }
322    if (!UpdateParamForCheckRemoteSignProfile(param)) {
323        return false;
324    }
325    return true;
326}
327
328int CmdUtil::GetCommandParameterKey(const char strChar, std::string& strChars, std::vector<std::string>& trustList,
329                                    std::string& keyStandBy)
330{
331    if (strChar == '-') {
332        bool isTrust = std::find(trustList.begin(), trustList.end(), strChars) != trustList.end();
333        if (!isTrust) {
334            PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "There is no '"
335                                + strChars + "' command for the trust list");
336            return RET_FAILED;
337        }
338        keyStandBy = strChars.substr(1);
339    } else {
340        PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "'" + strChars
341                            + "' Parameters error, Param key - value must in pairs");
342        return RET_FAILED;
343    }
344
345    return RET_OK;
346}
347
348bool CmdUtil::Convert2Params(char** args, const size_t size, const ParamsSharedPtr& param)
349{
350    param->SetMethod(args[1]);
351    std::string keyStandBy = "";
352    bool readKey = true;
353    std::vector<std::string> trustList = ParamsTrustList::GetInstance().GetTrustList(args[1]);
354    if (trustList.empty()) {
355        return false;
356    }
357    std::string strChars;
358    for (size_t i = 2; i < size; i++) {
359        if (readKey) {
360            strChars = args[i];
361            if (GetCommandParameterKey(args[i][0], strChars, trustList, keyStandBy) == RET_OK) {
362                readKey = false;
363            } else {
364                return false;
365            }
366        } else {
367            bool success = ValidAndPutParam(param, keyStandBy, args[i]);
368            if (success) {
369                keyStandBy = "";
370                readKey = true;
371            } else {
372                return false;
373            }
374        }
375    }
376    if (!readKey) {
377        PrintErrorNumberMsg("INVALIDPARAM_ERROR", INVALIDPARAM_ERROR,
378                            "The last value of parameter cannot be omitted");
379        return false;
380    }
381    if (!UpdateParam(param)) {
382        return false;
383    }
384    return true;
385}
386
387bool CmdUtil::ValidAndPutParam(const ParamsSharedPtr& params, const std::string& key, char* value)
388{
389    std::string  str = "Pwd";
390    bool result = true;
391    if (key.empty()) {
392        PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
393                            "The command-line parameter key cannot be empty");
394        result = false;
395    } else if (strlen(value) == 0) {
396        PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
397                            "The command-line parameter value cannot be empty");
398        result = false;
399    } else if (params->GetOptions()->count(key)) {
400        PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR,
401                            "Duplicate command parameter are not allowed '" + key + "'");
402        result = false;
403    } else if (key.length() >= str.length() && key.substr(key.length() - INVALIDCHAR) == str) {
404        params->GetOptions()->emplace(key, value);
405    } else {
406        if (key == Options::KEY_ALIAS || key == Options::ISSUER_KEY_ALIAS) {
407            std::string keyAlias = value;
408            std::transform(keyAlias.begin(), keyAlias.end(), keyAlias.begin(),
409                           [](unsigned char c) { return std::tolower(c); });
410            params->GetOptions()->emplace(key, keyAlias);
411        } else {
412            params->GetOptions()->emplace(key, std::string(value));
413        }
414    }
415    return result;
416}
417
418bool CmdUtil::JudgeAlgType(const std::string& keyAlg)
419{
420    if (keyAlg != "ECC") {
421        PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "not supported '" + keyAlg + "' Key algorithms");
422        return false;
423    }
424    return true;
425}
426
427bool CmdUtil::JudgeSize(const int size)
428{
429    if (size != NIST_P_256 && size != NIST_P_384) {
430        PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "Keysize params is incorrect, Support only 256 or 384");
431        return false;
432    }
433    return true;
434}
435
436bool CmdUtil::JudgeSignAlgType(const std::string& signAlg)
437{
438    if (signAlg != SIGN_ALG_SHA256 && signAlg != SIGN_ALG_SHA384) {
439        PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR, "not supported '" + signAlg + "' signature algorithm");
440        return  false;
441    }
442    return true;
443}
444
445/**
446 * @tc.name: Test parameter function
447 * @tc.desc: Pass more than one parameter,but it needs to be in the parameter list.
448 * @tc.type: FUNC
449 */
450bool CmdUtil::VerifyTypes(const std::string& inputType)
451{
452    if (inputType.size() == 0) {
453        return false;
454    }
455    std::vector<std::string> vecs = StringUtils::SplitString(inputType.c_str(), ',');
456    std::set<std::string> sets;
457    sets.insert("digitalSignature");
458    sets.insert("nonRepudiation");
459    sets.insert("keyEncipherment");
460    sets.insert("dataEncipherment");
461    sets.insert("keyAgreement");
462    sets.insert("certificateSignature");
463    sets.insert("crlSignature");
464    sets.insert("encipherOnly");
465    sets.insert("decipherOnly");
466    for (const auto& val : vecs) {
467        if (sets.count(val) == 0) {
468            PrintErrorNumberMsg("COMMAND_ERROR", COMMAND_ERROR,
469                                "Not support command param '" + val + "'");
470            return false;
471        }
472    }
473    return true;
474}
475
476/**
477 * @tc.name: Test parameter function
478 * @tc.desc: Pass one parameter,but it needs to be in the parameter list.
479 * @tc.type: FUNC
480 */
481bool CmdUtil::VerifyType(const std::string& inputType)
482{
483    std::set<std::string> sets;
484    sets.insert("clientAuthentication");
485    sets.insert("serverAuthentication");
486    sets.insert("codeSignature");
487    sets.insert("emailProtection");
488    sets.insert("smartCardLogin");
489    sets.insert("timestamp");
490    sets.insert("ocspSignature");
491    if (sets.count(inputType) == 0) {
492        PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
493                            "Not support command param '" + inputType + "'");
494        return false;
495    }
496    return true;
497}
498
499bool CmdUtil::VerifyType(const std::string& inputType, const std::string& supportTypes)
500{
501    std::string firstStr = supportTypes.substr(0, supportTypes.find_last_of(","));
502    std::string secondStr = supportTypes.substr(supportTypes.find_first_of(",") + 1,
503                                                supportTypes.size() - supportTypes.find_first_of(","));
504    if (inputType == firstStr || inputType == secondStr) {
505        return true;
506    }
507    PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, "Not support command param '" + inputType + "'");
508
509    return false;
510}
511} // namespace SignatureTools
512} // namespace OHOS