1 /*
2  * Copyright (c) 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 <vector>
17 #include "napi_object.h"
18 
19 #include "securec.h"
20 
21 #include "cf_log.h"
22 #include "cf_memory.h"
23 #include "cf_param.h"
24 #include "cf_result.h"
25 
26 #include "napi_cert_utils.h"
27 #include "napi_common.h"
28 
29 using namespace std;
30 
31 namespace OHOS {
32 namespace CertFramework {
33 constexpr size_t MAX_ARGS_COUNT = 5;
34 
35 constexpr uint32_t NAPI_OUT_TYPE_BLOB = 1;
36 constexpr uint32_t NAPI_OUT_TYPE_ARRAY = 2;
37 constexpr uint32_t NAPI_OUT_TYPE_NUMBER = 3;
38 constexpr uint32_t NAPI_OUT_TYPE_ENCODING_BLOB = 4;
39 constexpr uint32_t NAPI_OUT_TYPE_BOOL = 5;
40 
41 constexpr size_t PARAM_INDEX_0 = 0;
42 constexpr size_t PARAM_INDEX_1 = 1;
43 constexpr size_t PARAM_INDEX_2 = 2;
44 constexpr size_t PARAM_INDEX_3 = 3;
45 constexpr size_t PARAM_INDEX_4 = 4;
46 
47 constexpr size_t PARAM_COUNT_CERT_GET_ITEM = 1;
48 constexpr size_t PARAM_COUNT_EXT_GET_OIDS = 1;
49 constexpr size_t PARAM_COUNT_EXT_GET_ENTRY = 2;
50 constexpr size_t PARAM_COUNT_EXT_GET_ITEM = 0;
51 constexpr size_t PARAM_COUNT_EXT_CHECK_CA = 0;
52 constexpr size_t PARAM_COUNT_EXT_HAS_UN_SUPPORT = 0;
53 
54 struct CfInputParamsMap {
55     int32_t opType;
56     int32_t type;
57     size_t paramsCnt;
58     napi_valuetype expectedType[MAX_ARGS_COUNT];
59 };
60 
61 struct CfParamTagMap {
62     size_t index;
63     napi_valuetype valueType;
64     CfTag tag;
65 };
66 
67 struct CfResultMap {
68     int32_t opType;
69     int32_t type;
70     CfTag resultType;
71     uint32_t outType;
72 };
73 
74 const struct CfInputParamsMap INPUT_PARAMS_MAP[] = {
75     { OPERATION_TYPE_GET, CF_GET_TYPE_CERT_ITEM, PARAM_COUNT_CERT_GET_ITEM, { napi_number } },
76     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_OIDS, PARAM_COUNT_EXT_GET_OIDS, { napi_number } },
77     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ENTRY, PARAM_COUNT_EXT_GET_ENTRY, { napi_number, napi_object } },
78     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ITEM, PARAM_COUNT_EXT_GET_ITEM, { napi_undefined } },
79     { OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_CA, PARAM_COUNT_EXT_CHECK_CA, { napi_undefined } },
80     { OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_HAS_UN_SUPPORT, PARAM_COUNT_EXT_HAS_UN_SUPPORT, { napi_undefined } },
81 };
82 
83 const struct CfParamTagMap TAG_MAP[] = {
84     { PARAM_INDEX_0, napi_object, CF_TAG_PARAM0_BUFFER },
85     { PARAM_INDEX_1, napi_object, CF_TAG_PARAM1_BUFFER },
86     { PARAM_INDEX_2, napi_object, CF_TAG_PARAM2_BUFFER },
87     { PARAM_INDEX_3, napi_object, CF_TAG_PARAM3_BUFFER },
88     { PARAM_INDEX_4, napi_object, CF_TAG_PARAM4_BUFFER },
89     { PARAM_INDEX_0, napi_number, CF_TAG_PARAM0_INT32 },
90     { PARAM_INDEX_1, napi_number, CF_TAG_PARAM1_INT32 },
91     { PARAM_INDEX_2, napi_number, CF_TAG_PARAM2_INT32 },
92     { PARAM_INDEX_3, napi_number, CF_TAG_PARAM3_INT32 },
93     { PARAM_INDEX_4, napi_number, CF_TAG_PARAM4_INT32 },
94 };
95 
96 const struct CfResultMap RESULT_MAP[] = {
97     { OPERATION_TYPE_GET, CF_GET_TYPE_CERT_ITEM, CF_TAG_RESULT_BYTES, NAPI_OUT_TYPE_BLOB },
98     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_OIDS, CF_TAG_RESULT_BYTES, NAPI_OUT_TYPE_ARRAY },
99     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ENTRY, CF_TAG_RESULT_BYTES, NAPI_OUT_TYPE_BLOB },
100     { OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ITEM, CF_TAG_RESULT_BYTES, NAPI_OUT_TYPE_ENCODING_BLOB },
101     { OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_CA, CF_TAG_RESULT_INT, NAPI_OUT_TYPE_NUMBER },
102     { OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_HAS_UN_SUPPORT, CF_TAG_RESULT_BOOL, NAPI_OUT_TYPE_BOOL },
103 };
104 
FreeParsedParams(vector<CfParam> & params)105 static void FreeParsedParams(vector<CfParam> &params)
106 {
107     CfParam *param = params.data();
108     size_t paramCount = params.size();
109     if (param == nullptr) {
110         return;
111     }
112     while (paramCount > 0) {
113         paramCount--;
114         if ((param->tag & CF_TAG_TYPE_MASK) == CF_TAG_TYPE_BYTES) {
115             CF_FREE_PTR(param->blob.data);
116             param->blob.size = 0;
117         }
118         ++param;
119     }
120 }
121 
GetTagValue(size_t index,napi_valuetype valueType)122 static CfTag GetTagValue(size_t index, napi_valuetype valueType)
123 {
124     uint32_t count = sizeof(TAG_MAP) / sizeof(TAG_MAP[0]);
125     for (uint32_t i = 0; i < count; ++i) {
126         if ((index == TAG_MAP[i].index) && (valueType == TAG_MAP[i].valueType)) {
127             return TAG_MAP[i].tag;
128         }
129     }
130     return CF_TAG_INVALID;
131 }
132 
GetInputObject(napi_env env,napi_value object,size_t index,vector<CfParam> & params)133 static int32_t GetInputObject(napi_env env, napi_value object, size_t index, vector<CfParam> &params)
134 {
135     CfBlob *inBlob = CertGetBlobFromNapiValue(env, object);
136     if (inBlob == nullptr) {
137         CF_LOG_E("get blob failed");
138         return CF_INVALID_PARAMS;
139     }
140 
141     CfParam param;
142     param.tag = GetTagValue(index, napi_object);
143     param.blob.data = inBlob->data;
144     param.blob.size = inBlob->size;
145     params.push_back(param);
146 
147     CfFree(inBlob); /* inBlob's data need freed by caller */
148     return CF_SUCCESS;
149 }
150 
GetInputNumber(napi_env env,napi_value object,size_t index,vector<CfParam> & params)151 static int32_t GetInputNumber(napi_env env, napi_value object, size_t index, vector<CfParam> &params)
152 {
153     CfParam param;
154     napi_status status = napi_get_value_int32(env, object, &param.int32Param);
155     if (status != napi_ok) {
156         CF_LOG_E("can not get int value");
157         return CF_INVALID_PARAMS;
158     }
159 
160     param.tag = GetTagValue(index, napi_number);
161     params.push_back(param);
162     return CF_SUCCESS;
163 }
164 
GetInputParams(napi_env env,napi_value object,size_t index,vector<CfParam> & params)165 static int32_t GetInputParams(napi_env env, napi_value object, size_t index, vector<CfParam> &params)
166 {
167     napi_valuetype valueType = napi_undefined;
168     napi_status status = napi_typeof(env, object, &valueType);
169     if (status != napi_ok) {
170         CF_LOG_E("could not get object type");
171         return CF_INVALID_PARAMS;
172     }
173 
174     if (valueType == napi_object) {
175         return GetInputObject(env, object, index, params);
176     } else if (valueType == napi_number) {
177         return GetInputNumber(env, object, index, params);
178     } else {
179         return CF_INVALID_PARAMS;
180     }
181 }
182 
AddParams(const vector<CfParam> & params,CfParamSet * & paramSet)183 static int32_t AddParams(const vector<CfParam> &params, CfParamSet *&paramSet)
184 {
185     const CfParam *param = params.data();
186     size_t paramCount = params.size();
187     if (param == nullptr) {
188         return CF_SUCCESS;
189     }
190 
191     for (uint32_t i = 0; i < paramCount; ++i) {
192         int32_t ret = CfAddParams(paramSet, param, 1);
193         if (ret != CF_SUCCESS) {
194             CF_LOG_E("add param[%u] failed", i);
195             return ret;
196         }
197         param++;
198     }
199     return CF_SUCCESS;
200 }
201 
ConstructInParamSet(const vector<CfParam> & params,CfParamSet * & inParamSet)202 static int32_t ConstructInParamSet(const vector<CfParam> &params, CfParamSet *&inParamSet)
203 {
204     CfParamSet *tmp = NULL;
205     int32_t ret = CfInitParamSet(&tmp);
206     if (ret != CF_SUCCESS) {
207         CF_LOG_E("init paramSet failed");
208         return ret;
209     }
210 
211     ret = AddParams(params, tmp);
212     if (ret != CF_SUCCESS) {
213         CfFreeParamSet(&tmp);
214         return ret;
215     }
216 
217     ret = CfBuildParamSet(&tmp);
218     if (ret != CF_SUCCESS) {
219         CF_LOG_E("build paramSet failed");
220         CfFreeParamSet(&tmp);
221         return ret;
222     }
223 
224     inParamSet = tmp;
225     return CF_SUCCESS;
226 }
227 
ConstructTypeParams(int32_t opType,int32_t typeValue,vector<CfParam> & params)228 static void ConstructTypeParams(int32_t opType, int32_t typeValue, vector<CfParam> &params)
229 {
230     CfParam param;
231     if (opType == OPERATION_TYPE_GET) {
232         param.tag = CF_TAG_GET_TYPE;
233         param.int32Param = typeValue;
234     } else { /* is check */
235         param.tag = CF_TAG_CHECK_TYPE;
236         param.int32Param = typeValue;
237     }
238     params.push_back(param);
239 }
240 
CheckParamsNapiType(napi_env env,napi_value * argv,size_t argc,napi_valuetype const * expectedType,size_t expectedCnt)241 static int32_t CheckParamsNapiType(napi_env env, napi_value *argv, size_t argc,
242     napi_valuetype const *expectedType, size_t expectedCnt)
243 {
244     if (argc != expectedCnt) {
245         CF_LOG_E("params count invalid");
246         return CF_INVALID_PARAMS;
247     }
248 
249     for (size_t i = 0; i < argc; ++i) {
250         napi_valuetype valueType = napi_undefined;
251         napi_status status = napi_typeof(env, argv[i], &valueType);
252         if (status != napi_ok) {
253             CF_LOG_E("could not get object type");
254             return CF_INVALID_PARAMS;
255         }
256 
257         if (valueType != expectedType[i]) {
258             CF_LOG_E("input object type invalid");
259             return CF_INVALID_PARAMS;
260         }
261     }
262 
263     return CF_SUCCESS;
264 }
265 
CheckInputParams(napi_env env,napi_value * argv,size_t argc,int32_t opType,int32_t typeValue)266 static int32_t CheckInputParams(napi_env env, napi_value *argv, size_t argc, int32_t opType, int32_t typeValue)
267 {
268     for (uint32_t i = 0; i < sizeof(INPUT_PARAMS_MAP) / sizeof(INPUT_PARAMS_MAP[0]); ++i) {
269         if ((opType == INPUT_PARAMS_MAP[i].opType) && (typeValue == INPUT_PARAMS_MAP[i].type)) {
270             if (CheckParamsNapiType(env, argv, argc, INPUT_PARAMS_MAP[i].expectedType,
271                 INPUT_PARAMS_MAP[i].paramsCnt) != CF_SUCCESS) {
272                 return CF_INVALID_PARAMS;
273             }
274             return CF_SUCCESS;
275         }
276     }
277     return CF_INVALID_PARAMS;
278 }
279 
GetInParamSet(napi_env env,napi_callback_info info,int32_t opType,int32_t typeValue,CfParamSet * & inParamSet)280 static int32_t GetInParamSet(napi_env env, napi_callback_info info, int32_t opType, int32_t typeValue,
281     CfParamSet *&inParamSet)
282 {
283     size_t argc = MAX_ARGS_COUNT;
284     napi_value argv[MAX_ARGS_COUNT] = { 0 };
285     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
286 
287     int32_t ret = CheckInputParams(env, argv, argc, opType, typeValue);
288     if (ret != CF_SUCCESS) {
289         CF_LOG_E("input params invalid");
290         return CF_INVALID_PARAMS;
291     }
292 
293     vector<CfParam> params;
294     ConstructTypeParams(opType, typeValue, params);
295 
296     for (size_t i = 0; i < argc; ++i) {
297         ret = GetInputParams(env, argv[i], i, params);
298         if (ret != CF_SUCCESS) {
299             FreeParsedParams(params);
300             CF_LOG_E("param[%u] invalid", i);
301             return ret;
302         }
303     }
304 
305     /* ext get encoded */
306     if ((typeValue == CF_GET_TYPE_EXT_ITEM) && (argc == 0)) {
307         CfParam paramExtEncoded = { .tag = CF_TAG_PARAM0_INT32, .int32Param = CF_ITEM_ENCODED };
308         params.push_back(paramExtEncoded);
309     }
310 
311     ret = ConstructInParamSet(params, inParamSet);
312     FreeParsedParams(params);
313     if (ret != CF_SUCCESS) {
314         CF_LOG_E("construct In paramSet failed");
315         return ret;
316     }
317 
318     return CF_SUCCESS;
319 }
320 
GetResultType(int32_t opType,int32_t typeValue,CfTag & resultType,uint32_t & outType)321 static int32_t GetResultType(int32_t opType, int32_t typeValue, CfTag &resultType, uint32_t &outType)
322 {
323     for (uint32_t i = 0; i < sizeof(RESULT_MAP) / sizeof(RESULT_MAP[0]); ++i) {
324         if ((typeValue == RESULT_MAP[i].type) && (opType == RESULT_MAP[i].opType)) {
325             resultType = RESULT_MAP[i].resultType;
326             outType = RESULT_MAP[i].outType;
327             return CF_SUCCESS;
328         }
329     }
330     return CF_INVALID_PARAMS;
331 }
332 
CheckResultType(const CfParamSet * paramSet,CfTag resultType)333 static int32_t CheckResultType(const CfParamSet *paramSet, CfTag resultType)
334 {
335     CfParam *resultTypeParam = NULL;
336     int32_t ret = CfGetParam(paramSet, CF_TAG_RESULT_TYPE, &resultTypeParam);
337     if (ret != CF_SUCCESS) {
338         CF_LOG_E("get CF_TAG_RESULT_TYPE failed.");
339         return ret;
340     }
341 
342     if (resultTypeParam->int32Param != (resultType & CF_TAG_TYPE_MASK)) {
343         CF_LOG_E("result type[0x%x] is not [0x%x].", resultTypeParam->int32Param, resultType);
344         return CF_INVALID_PARAMS;
345     }
346 
347     return CF_SUCCESS;
348 }
349 
ConvertToNapiValue(napi_env env,int32_t opType,int32_t typeValue,const CfParamSet * paramSet)350 static napi_value ConvertToNapiValue(napi_env env, int32_t opType, int32_t typeValue, const CfParamSet *paramSet)
351 {
352     CfTag resultType = CF_TAG_INVALID;
353     uint32_t outType = 0;
354     int32_t ret = GetResultType(opType, typeValue, resultType, outType);
355     if (ret != CF_SUCCESS) {
356         CF_LOG_E("get result type failed.");
357         return nullptr;
358     }
359 
360     ret = CheckResultType(paramSet, resultType);
361     if (ret != CF_SUCCESS) {
362         return nullptr;
363     }
364 
365     CfParam *resultParam = NULL;
366     ret = CfGetParam(paramSet, resultType, &resultParam);
367     if (ret != CF_SUCCESS) {
368         CF_LOG_E("get [0x%x] from param failed.", resultType);
369         return nullptr;
370     }
371 
372     if (outType == NAPI_OUT_TYPE_BLOB) {
373         return CertConvertBlobToNapiValue(env, &resultParam->blob);
374     } else if (outType == NAPI_OUT_TYPE_ARRAY) {
375         return ConvertBlobArrayToNapiValue(env, paramSet);
376     } else if (outType == NAPI_OUT_TYPE_NUMBER) {
377         napi_value result = nullptr;
378         napi_create_int32(env, resultParam->int32Param, &result);
379         return result;
380     } else if (outType == NAPI_OUT_TYPE_ENCODING_BLOB) {
381         CfEncodingBlob encoded = { resultParam->blob.data, resultParam->blob.size, CF_FORMAT_DER };
382         return ConvertEncodingBlobToNapiValue(env, &encoded);
383     } else if (outType == NAPI_OUT_TYPE_BOOL) {
384         napi_value result = nullptr;
385         napi_get_boolean(env, resultParam->boolParam, &result);
386         return result;
387     }
388 
389     return nullptr;
390 }
391 
DoOperation(const CfObject * obj,int32_t opType,const CfParamSet * inParamSet,CfParamSet ** outParamSet)392 static int32_t DoOperation(const CfObject *obj, int32_t opType, const CfParamSet *inParamSet,
393     CfParamSet **outParamSet)
394 {
395     int32_t ret = CF_INVALID_PARAMS;
396     if (opType == OPERATION_TYPE_GET) {
397         ret = obj->get(obj, inParamSet, outParamSet);
398     } else if (opType == OPERATION_TYPE_CHECK) {
399         ret = obj->check(obj, inParamSet, outParamSet);
400     }
401     if (ret != CF_SUCCESS) {
402         CF_LOG_E("do operation[%d] failed", opType);
403     }
404     return ret;
405 }
406 
CommonOperation(napi_env env,napi_callback_info info,const CfObject * obj,int32_t opType,int32_t typeValue)407 napi_value CommonOperation(napi_env env, napi_callback_info info, const CfObject *obj,
408     int32_t opType, int32_t typeValue)
409 {
410     CfParamSet *inParamSet = NULL;
411     int32_t ret = GetInParamSet(env, info, opType, typeValue, inParamSet);
412     if (ret != CF_SUCCESS) {
413         napi_throw(env, CertGenerateBusinessError(env, ret, "get param failed"));
414         return nullptr;
415     }
416 
417     CfParamSet *outParamSet = NULL;
418     ret = DoOperation(obj, opType, inParamSet, &outParamSet);
419     CfFreeParamSet(&inParamSet);
420     if (ret != CF_SUCCESS) {
421         napi_throw(env, CertGenerateBusinessError(env, ret, "do operation failed"));
422         return nullptr;
423     }
424 
425     napi_value returnValue = ConvertToNapiValue(env, opType, typeValue, outParamSet);
426     if (returnValue == nullptr) {
427         napi_throw(env, CertGenerateBusinessError(env, CF_INVALID_PARAMS, "construct result failed"));
428     }
429     CfFreeParamSet(&outParamSet);
430     return returnValue;
431 }
432 }  // namespace CertFramework
433 }  // namespace OHOS
434