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 "napi_cert_extension.h"
17 
18 #include "napi/native_common.h"
19 #include "napi/native_api.h"
20 
21 #include "cf_api.h"
22 #include "cf_log.h"
23 #include "cf_memory.h"
24 #include "cf_param.h"
25 #include "cf_result.h"
26 
27 #include "napi_cert_defines.h"
28 #include "napi_cert_utils.h"
29 #include "napi_common.h"
30 #include "napi_object.h"
31 
32 namespace OHOS {
33 namespace CertFramework {
34 thread_local napi_ref NapiCertExtension::classRef_ = nullptr;
35 
36 struct CfExtensionAsyncContext {
37     AsyncCtx async = nullptr;
38 
39     CfEncodingBlob *encodingBlob = nullptr;
40     CfObject *extsObj = nullptr;
41 };
42 using ExtsAsyncContext = CfExtensionAsyncContext *;
43 
NapiCertExtension(CfObject * object)44 NapiCertExtension::NapiCertExtension(CfObject *object)
45 {
46     this->object_ = object;
47 }
48 
~NapiCertExtension()49 NapiCertExtension::~NapiCertExtension()
50 {
51     if (this->object_ != nullptr) {
52         this->object_->destroy(&this->object_);
53     }
54 }
55 
NewExtsAsyncContext(void)56 static ExtsAsyncContext NewExtsAsyncContext(void)
57 {
58     ExtsAsyncContext extsAsyncCtx = static_cast<ExtsAsyncContext>(CfMalloc(sizeof(CfExtensionAsyncContext), 0));
59     if (extsAsyncCtx == nullptr) {
60         CF_LOG_E("Failed to malloc extension async context");
61         return nullptr;
62     }
63 
64     AsyncCtx asyncCtx = static_cast<AsyncCtx>(CfMalloc(sizeof(AsyncContext), 0));
65     if (asyncCtx == nullptr) {
66         CF_LOG_E("Failed to malloc async context");
67         CfFree(extsAsyncCtx);
68         return nullptr;
69     }
70 
71     extsAsyncCtx->async = asyncCtx;
72     return extsAsyncCtx;
73 }
74 
DeleteExtsAsyncContext(napi_env env,ExtsAsyncContext & context)75 static void DeleteExtsAsyncContext(napi_env env, ExtsAsyncContext &context)
76 {
77     if (context == nullptr) {
78         return;
79     }
80 
81     FreeAsyncContext(env, context->async);
82 
83     CfEncodingBlobDataFree(context->encodingBlob);
84     CfFree(context->encodingBlob);
85     context->encodingBlob = nullptr;
86 
87     CfFree(context);
88 }
89 
ParseCreateExtsJSParams(napi_env env,napi_callback_info info,ExtsAsyncContext context)90 static napi_value ParseCreateExtsJSParams(napi_env env, napi_callback_info info, ExtsAsyncContext context)
91 {
92     size_t argc = ARGS_SIZE_TWO;
93     napi_value argv[ARGS_SIZE_TWO] = { nullptr };
94     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
95     if (!CertCheckArgsCount(env, argc, ARGS_SIZE_TWO, false)) {
96         return nullptr;
97     }
98 
99     if (!GetEncodingBlobFromValue(env, argv[PARAM0], &context->encodingBlob)) {
100         CF_LOG_E("get encoding blob from data failed!");
101         return nullptr;
102     }
103 
104     context->async->asyncType = GetAsyncType(env, argc, ARGS_SIZE_TWO, argv[PARAM1]);
105     if (!GetCallbackAndPromise(env, context->async, argv[PARAM1])) {
106         return nullptr;
107     }
108 
109     return NapiGetInt32(env, 0);
110 }
111 
CreateCertExtsJSInstance(napi_env env)112 static napi_value CreateCertExtsJSInstance(napi_env env)
113 {
114     napi_value constructor = nullptr;
115     napi_value instance = nullptr;
116     napi_get_reference_value(env, NapiCertExtension::classRef_, &constructor);
117     napi_new_instance(env, constructor, 0, nullptr, &instance);
118     return instance;
119 }
120 
CreateCertExtsExecute(napi_env env,void * data)121 static void CreateCertExtsExecute(napi_env env, void *data)
122 {
123     ExtsAsyncContext context = static_cast<ExtsAsyncContext>(data);
124     context->async->errCode = CfCreate(CF_OBJ_TYPE_EXTENSION, context->encodingBlob, &context->extsObj);
125     if (context->async->errCode != CF_SUCCESS) {
126         context->async->errMsg = "create extension failed";
127     }
128 }
129 
CreateCertExtsComplete(napi_env env,napi_status status,void * data)130 static void CreateCertExtsComplete(napi_env env, napi_status status, void *data)
131 {
132     ExtsAsyncContext context = static_cast<ExtsAsyncContext>(data);
133     if (context->async->errCode != CF_SUCCESS) {
134         ReturnJSResult(env, context->async, nullptr);
135         DeleteExtsAsyncContext(env, context);
136         return;
137     }
138 
139     napi_value jsObject = CreateCertExtsJSInstance(env);
140     NapiCertExtension *napiObject = new (std::nothrow) NapiCertExtension(context->extsObj);
141     if (napiObject == nullptr) {
142         context->async->errCode = CF_ERR_MALLOC;
143         context->async->errMsg = "Failed to create napi extension class";
144         CF_LOG_E("Failed to create napi extension class");
145         if (context->extsObj != nullptr) {
146             context->extsObj->destroy(&(context->extsObj));
147         }
148         ReturnJSResult(env, context->async, nullptr);
149         DeleteExtsAsyncContext(env, context);
150         return;
151     }
152     napi_wrap(
153         env, jsObject, napiObject,
154         [](napi_env env, void *data, void *hint) {
155             NapiCertExtension *certExts = static_cast<NapiCertExtension *>(data);
156             delete certExts;
157             return;
158         },
159         nullptr, nullptr);
160 
161     ReturnJSResult(env, context->async, jsObject);
162     DeleteExtsAsyncContext(env, context);
163 }
164 
CreateCertExtsAsyncWork(napi_env env,ExtsAsyncContext context)165 static napi_value CreateCertExtsAsyncWork(napi_env env, ExtsAsyncContext context)
166 {
167     napi_create_async_work(
168         env,
169         nullptr,
170         GetResourceName(env, "CreateCertExtsAsyncWork"),
171         CreateCertExtsExecute,
172         CreateCertExtsComplete,
173         static_cast<void *>(context),
174         &context->async->asyncWork);
175 
176     napi_queue_async_work(env, context->async->asyncWork);
177     if (context->async->asyncType == ASYNC_TYPE_PROMISE) {
178         return context->async->promise;
179     } else {
180         return NapiGetNull(env);
181     }
182     return nullptr;
183 }
184 
NapiCreateCertExtension(napi_env env,napi_callback_info info)185 napi_value NapiCreateCertExtension(napi_env env, napi_callback_info info)
186 {
187     ExtsAsyncContext context = NewExtsAsyncContext();
188     if (context == nullptr) {
189         CF_LOG_E("Failed to new create exts async context");
190         return nullptr;
191     }
192 
193     napi_value result = ParseCreateExtsJSParams(env, info, context);
194     if (result == nullptr) {
195         CF_LOG_E("Failed to parse JS params for create exts object");
196         DeleteExtsAsyncContext(env, context);
197         return nullptr;
198     }
199 
200     result = CreateCertExtsAsyncWork(env, context);
201     if (result == nullptr) {
202         CF_LOG_E("Failed to create exts object in async work");
203         DeleteExtsAsyncContext(env, context);
204         return nullptr;
205     }
206 
207     return result;
208 }
209 
NapiCommonOperation(napi_env env,napi_callback_info info,int32_t opType,int32_t typeValue)210 static napi_value NapiCommonOperation(napi_env env, napi_callback_info info, int32_t opType, int32_t typeValue)
211 {
212     napi_value jsObject = nullptr;
213     napi_get_cb_info(env, info, nullptr, nullptr, &jsObject, nullptr);
214 
215     NapiCertExtension *napiExtsObj = nullptr;
216     napi_unwrap(env, jsObject, reinterpret_cast<void **>(&napiExtsObj));
217     if (napiExtsObj == nullptr) {
218         CF_LOG_E("napi extension objtect is nullptr!");
219         return nullptr;
220     }
221 
222     CfObject *extsObj = napiExtsObj->GetObject();
223     if (extsObj == nullptr) {
224         CF_LOG_E("cf objtect is nullptr!");
225         return nullptr;
226     }
227     return CommonOperation(env, info, extsObj, opType, typeValue);
228 }
229 
NapiGetExtensionEncoded(napi_env env,napi_callback_info info)230 napi_value NapiGetExtensionEncoded(napi_env env, napi_callback_info info)
231 {
232     return NapiCommonOperation(env, info, OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ITEM);
233 }
234 
NapiGetExtensionOidList(napi_env env,napi_callback_info info)235 static napi_value NapiGetExtensionOidList(napi_env env, napi_callback_info info)
236 {
237     return NapiCommonOperation(env, info, OPERATION_TYPE_GET, CF_GET_TYPE_EXT_OIDS);
238 }
239 
NapiGetExtensionEntry(napi_env env,napi_callback_info info)240 static napi_value NapiGetExtensionEntry(napi_env env, napi_callback_info info)
241 {
242     return NapiCommonOperation(env, info, OPERATION_TYPE_GET, CF_GET_TYPE_EXT_ENTRY);
243 }
244 
NapiExtensionCheckCA(napi_env env,napi_callback_info info)245 static napi_value NapiExtensionCheckCA(napi_env env, napi_callback_info info)
246 {
247     return NapiCommonOperation(env, info, OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_CA);
248 }
249 
NapiExtensionHasUnsupportCritical(napi_env env,napi_callback_info info)250 static napi_value NapiExtensionHasUnsupportCritical(napi_env env, napi_callback_info info)
251 {
252     return NapiCommonOperation(env, info, OPERATION_TYPE_CHECK, CF_CHECK_TYPE_EXT_HAS_UN_SUPPORT);
253 }
254 
CertExtsConstructor(napi_env env,napi_callback_info info)255 static napi_value CertExtsConstructor(napi_env env, napi_callback_info info)
256 {
257     napi_value thisVar = nullptr;
258     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
259     return thisVar;
260 }
261 
DefineCertExtensionJsClass(napi_env env,napi_value exports)262 void NapiCertExtension::DefineCertExtensionJsClass(napi_env env, napi_value exports)
263 {
264     napi_property_descriptor desc[] = {
265         DECLARE_NAPI_FUNCTION("createCertExtension", NapiCreateCertExtension),
266     };
267     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
268 
269     napi_property_descriptor CertExtensionDesc[] = {
270         DECLARE_NAPI_FUNCTION("getEncoded", NapiGetExtensionEncoded),
271         DECLARE_NAPI_FUNCTION("getOidList", NapiGetExtensionOidList),
272         DECLARE_NAPI_FUNCTION("getEntry", NapiGetExtensionEntry),
273         DECLARE_NAPI_FUNCTION("checkCA", NapiExtensionCheckCA),
274         DECLARE_NAPI_FUNCTION("hasUnsupportedCriticalExtension", NapiExtensionHasUnsupportCritical),
275     };
276 
277     napi_value constructor = nullptr;
278     napi_define_class(
279         env,
280         "CertExtension",
281         NAPI_AUTO_LENGTH,
282         CertExtsConstructor,
283         nullptr,
284         sizeof(CertExtensionDesc) / sizeof(CertExtensionDesc[0]),
285         CertExtensionDesc,
286         &constructor);
287     napi_create_reference(env, constructor, 1, &classRef_);
288 }
289 } // namespace CertFramework
290 } // namespace OHOS
291