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 
23 namespace OHOS {
24 namespace SignatureTools {
25 const std::regex INTEGER_PATTERN = std::regex("\\d{1,10}");
26 
String2Bool(Options* options, const std::string& option)27 bool 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 
UpdateParamForVariantCertInt(const ParamsSharedPtr& param)42 static 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 
UpdateParamForVariantInt(const ParamsSharedPtr& param)74 static 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 
UpdateParamForVariantBoolKeyUsage(const ParamsSharedPtr& param)108 static 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 
UpdateParamForVariantBoolProfileSigned(const ParamsSharedPtr& param)132 static 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 
UpdateParamForCheckOutFile(Options* options, const std::initializer_list<std::string>& outFileKeys)155 bool 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 
UpdateParamForCheckInFile(Options* options, const std::initializer_list<std::string>& inFileKeys)196 bool 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 
UpdateParamForCheckSignAlg(const ParamsSharedPtr& param)225 static 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 
UpdateParamForInform(const ParamsSharedPtr& param)239 static 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 
UpdateParamForOutform(const ParamsSharedPtr& param)259 static 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
UpdateParamForCheckRemoteSignProfile(const ParamsSharedPtr& param)280 static 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 
UpdateParam(const ParamsSharedPtr& param)302 static 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 
GetCommandParameterKey(const char strChar, std::string& strChars, std::vector<std::string>& trustList, std::string& keyStandBy)328 int 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 
Convert2Params(char** args, const size_t size, const ParamsSharedPtr& param)348 bool 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 
ValidAndPutParam(const ParamsSharedPtr& params, const std::string& key, char* value)387 bool 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 
JudgeAlgType(const std::string& keyAlg)418 bool 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 
JudgeSize(const int size)427 bool 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 
JudgeSignAlgType(const std::string& signAlg)436 bool 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  */
VerifyTypes(const std::string& inputType)450 bool 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  */
VerifyType(const std::string& inputType)481 bool 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 
VerifyType(const std::string& inputType, const std::string& supportTypes)499 bool 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