1/*
2 * Copyright (C) 2022 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 "idm_session.h"
17
18#include <inttypes.h>
19#include "securec.h"
20
21#include "adaptor_algorithm.h"
22#include "adaptor_log.h"
23#include "adaptor_memory.h"
24#include "adaptor_time.h"
25#include "coauth.h"
26#include "linked_list.h"
27#include "idm_database.h"
28
29#define SESSION_VALIDITY_PERIOD (10 * 60 * 1000)
30#define MAX_CHALLENGE_GENERATION_TIMES 5
31
32#ifdef IAM_TEST_ENABLE
33#define IAM_STATIC
34#else
35#define IAM_STATIC static
36#endif
37
38// User IDM session information.
39struct SessionInfo {
40    int32_t userId;
41    uint32_t authType;
42    uint64_t time;
43    uint64_t validAuthTokenTime;
44    uint8_t challenge[CHALLENGE_LEN];
45    uint64_t scheduleId;
46    bool isUpdate;
47    bool isScheduleValid;
48} *g_session;
49
50IAM_STATIC Buffer *g_cacheRootSecret = NULL;
51
52IAM_STATIC void DestroyCacheRootSecret(void)
53{
54    DestoryBuffer(g_cacheRootSecret);
55    g_cacheRootSecret = NULL;
56}
57
58IAM_STATIC bool IsSessionExist(void)
59{
60    if (g_session == NULL) {
61        LOG_INFO("the session does not exist");
62        return false;
63    }
64    return true;
65}
66
67IAM_STATIC ResultCode GenerateChallenge(uint8_t *challenge, uint32_t challengeLen)
68{
69    for (uint32_t i = 0; i < MAX_CHALLENGE_GENERATION_TIMES; ++i) {
70        if (SecureRandom(challenge, challengeLen) != RESULT_SUCCESS) {
71            LOG_ERROR("get challenge failed");
72            return RESULT_GENERAL_ERROR;
73        }
74        for (uint32_t j = 0; j < challengeLen; j++) {
75            if (challenge[j] != 0) {
76                return RESULT_SUCCESS;
77            }
78        }
79        LOG_INFO("challenge is invalid, get again.");
80    }
81    LOG_ERROR("a rare failture");
82    return RESULT_GENERAL_ERROR;
83}
84
85ResultCode OpenEditSession(int32_t userId, uint8_t *challenge, uint32_t challengeLen)
86{
87    if (challenge == NULL || challengeLen != CHALLENGE_LEN) {
88        LOG_ERROR("challenge is null");
89        return RESULT_BAD_PARAM;
90    }
91    (void)memset_s(challenge, CHALLENGE_LEN, 0, CHALLENGE_LEN);
92    if (IsSessionExist()) {
93        (void)CloseEditSession();
94    }
95    g_session = Malloc(sizeof(struct SessionInfo));
96    if (g_session == NULL) {
97        LOG_ERROR("g_session malloc failed");
98        return RESULT_NO_MEMORY;
99    }
100    if (memset_s(g_session, sizeof(struct SessionInfo), 0, sizeof(struct SessionInfo)) != EOK) {
101        LOG_ERROR("g_session set failed");
102        Free(g_session);
103        g_session = NULL;
104        return RESULT_GENERAL_ERROR;
105    }
106    g_session->userId = userId;
107    ResultCode ret = GenerateChallenge(g_session->challenge, CHALLENGE_LEN);
108    if (ret != RESULT_SUCCESS) {
109        LOG_ERROR("failed to generate challenge");
110        Free(g_session);
111        g_session = NULL;
112        return ret;
113    }
114    g_session->time = GetSystemTime();
115    g_session->validAuthTokenTime = g_session->time;
116
117    if (memcpy_s(challenge, CHALLENGE_LEN, g_session->challenge, CHALLENGE_LEN) != EOK) {
118        LOG_ERROR("failed to copy challenge");
119        Free(g_session);
120        g_session = NULL;
121        return RESULT_BAD_COPY;
122    }
123    g_session->isScheduleValid = false;
124    return RESULT_SUCCESS;
125}
126
127void RefreshValidTokenTime(void)
128{
129    if (!IsSessionExist()) {
130        LOG_ERROR("session is invalid");
131        return;
132    }
133    g_session->validAuthTokenTime = GetSystemTime();
134}
135
136bool IsValidTokenTime(uint64_t tokenTime)
137{
138    if (!IsSessionExist()) {
139        LOG_ERROR("session is invalid");
140        return false;
141    }
142    return tokenTime >= g_session->validAuthTokenTime;
143}
144
145ResultCode CloseEditSession(void)
146{
147    if (!IsSessionExist()) {
148        return RESULT_GENERAL_ERROR;
149    }
150    DestroyCacheRootSecret();
151    ClearCachePin(g_session->userId);
152    Free(g_session);
153    g_session = NULL;
154    return RESULT_SUCCESS;
155}
156
157ResultCode GetUserId(int32_t *userId)
158{
159    if (userId == NULL || !IsSessionExist()) {
160        LOG_ERROR("param is invalid");
161        return RESULT_BAD_PARAM;
162    }
163    *userId = g_session->userId;
164    return RESULT_SUCCESS;
165}
166
167ResultCode CheckChallenge(uint8_t *challenge, uint32_t challengeLen)
168{
169    if (challenge == NULL || challengeLen != CHALLENGE_LEN) {
170        LOG_ERROR("param is invalid");
171        return RESULT_BAD_PARAM;
172    }
173    if (!IsSessionExist()) {
174        LOG_ERROR("param is invalid");
175        return RESULT_NEED_INIT;
176    }
177    if (memcmp(challenge, g_session->challenge, CHALLENGE_LEN) != EOK) {
178        LOG_ERROR("failed to compare challenge");
179        return RESULT_BAD_MATCH;
180    }
181    return RESULT_SUCCESS;
182}
183
184ResultCode AssociateCoauthSchedule(uint64_t scheduleId, uint32_t authType, bool isUpdate)
185{
186    if (!IsSessionExist()) {
187        return RESULT_NEED_INIT;
188    }
189    g_session->scheduleId = scheduleId;
190    g_session->authType = authType;
191    g_session->isUpdate = isUpdate;
192    g_session->isScheduleValid = true;
193    return RESULT_SUCCESS;
194}
195
196void BreakOffCoauthSchedule(void)
197{
198    if (!IsSessionExist()) {
199        return;
200    }
201    if (g_session->isScheduleValid) {
202        RemoveCoAuthSchedule(g_session->scheduleId);
203    }
204    g_session->isScheduleValid = false;
205}
206
207ResultCode GetEnrollScheduleInfo(uint64_t *scheduleId, uint32_t *authType)
208{
209    if (scheduleId == NULL || authType == NULL) {
210        LOG_ERROR("param is null");
211        return RESULT_BAD_PARAM;
212    }
213    if (!IsSessionExist() || g_session->isScheduleValid == false) {
214        return RESULT_NEED_INIT;
215    }
216    *scheduleId = g_session->scheduleId;
217    *authType = g_session->authType;
218    return RESULT_SUCCESS;
219}
220
221ResultCode CheckSessionTimeout(void)
222{
223    if (!IsSessionExist()) {
224        return RESULT_NEED_INIT;
225    }
226    uint64_t currentTime = GetSystemTime();
227    if (currentTime < g_session->time) {
228        LOG_ERROR("bad time, currentTime: %{public}" PRIu64 ", sessionTime: %{public}" PRIu64,
229            currentTime, g_session->time);
230        return RESULT_GENERAL_ERROR;
231    }
232    if (currentTime - g_session->time > SESSION_VALIDITY_PERIOD) {
233        LOG_ERROR("timeout, currentTime: %{public}" PRIu64 ", sessionTime: %{public}" PRIu64,
234            currentTime, g_session->time);
235        DestroyCacheRootSecret();
236        ClearCachePin(g_session->userId);
237        return RESULT_TIMEOUT;
238    }
239    return RESULT_SUCCESS;
240}
241
242ResultCode GetIsUpdate(bool *isUpdate)
243{
244    if (isUpdate == NULL) {
245        LOG_ERROR("param is invalid");
246        return RESULT_BAD_PARAM;
247    }
248    if (!IsSessionExist() || g_session->isScheduleValid == false) {
249        LOG_ERROR("session need init");
250        return RESULT_NEED_INIT;
251    }
252    *isUpdate = g_session->isUpdate;
253    return RESULT_SUCCESS;
254}
255
256ResultCode CheckSessionValid(int32_t userId)
257{
258    ResultCode ret = CheckSessionTimeout();
259    if (ret != RESULT_SUCCESS) {
260        return ret;
261    }
262    if (g_session->userId != userId) {
263        return RESULT_GENERAL_ERROR;
264    }
265    return RESULT_SUCCESS;
266}
267
268void CacheRootSecret(int32_t userId, Buffer *rootSecret)
269{
270    /* The presence of a session is the pin change phase */
271    if (CheckSessionTimeout() != RESULT_SUCCESS) {
272        return;
273    }
274    if (g_session->userId != userId) {
275        LOG_ERROR("CacheRootSecret check user id fail");
276        return;
277    }
278    if (!CheckBufferWithSize(rootSecret, ROOT_SECRET_LEN)) {
279        LOG_ERROR("check root secret fail");
280        return;
281    }
282    DestroyCacheRootSecret();
283    g_cacheRootSecret = CopyBuffer(rootSecret);
284    if (g_cacheRootSecret == NULL) {
285        LOG_ERROR("copy cache root secret fail");
286    }
287}
288
289Buffer *GetCacheRootSecret(int32_t userId)
290{
291    if (CheckSessionTimeout() != RESULT_SUCCESS) {
292        return NULL;
293    }
294    if (g_session->userId != userId) {
295        LOG_ERROR("GetCacheRootSecret check user id fail");
296        return NULL;
297    }
298    if (g_cacheRootSecret == NULL) {
299        LOG_ERROR("no cache root secret");
300        return NULL;
301    }
302    return CopyBuffer(g_cacheRootSecret);
303}
304
305ResultCode GetChallenge(uint8_t *challenge, uint32_t challengeLen)
306{
307    if ((challenge == NULL) || (challengeLen != CHALLENGE_LEN)) {
308        LOG_ERROR("challenge is invalid");
309        return RESULT_BAD_PARAM;
310    }
311
312    ResultCode ret = CheckSessionTimeout();
313    if (ret != RESULT_SUCCESS) {
314        LOG_ERROR("session does not exist");
315        return ret;
316    }
317    if (memcpy_s(challenge, challengeLen, g_session->challenge, CHALLENGE_LEN) != EOK) {
318        LOG_ERROR("copy challenge failed");
319        return RESULT_GENERAL_ERROR;
320    }
321
322    return RESULT_SUCCESS;
323}
324
325ResultCode IsValidUserType(int32_t userType)
326{
327    if (userType != MAIN_USER && userType != SUB_USER && userType != PRIVATE_USER) {
328        LOG_ERROR("userType is invalid");
329        return RESULT_BAD_PARAM;
330    }
331    LOG_INFO("userType is valid");
332    return RESULT_SUCCESS;
333}