1/*
2 * Copyright (c) 2022-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
16#include "scrypt.h"
17#include <openssl/ossl_typ.h>
18#include <openssl/kdf.h>
19#include "securec.h"
20#include <unordered_map>
21#include "iam_logger.h"
22
23#define LOG_TAG "PIN_AUTH_SDK"
24
25namespace OHOS {
26namespace UserIam {
27namespace PinAuth {
28namespace {
29constexpr uint32_t OUT_LENGTH = 64;
30constexpr uint32_t SCRYPT_N_V0 = 32768;
31constexpr uint32_t SCRYPT_N_V1 = 2048;
32constexpr uint32_t SCRYPT_R = 8;
33constexpr uint32_t SCRYPT_P = 1;
34constexpr uint32_t SCRYPT_P_V3 = 2;
35
36struct ScryptParameters {
37    int32_t scryptN;
38    int32_t scryptR;
39    int32_t scryptP;
40};
41
42std::unordered_map<uint32_t, ScryptParameters> g_version2Param_ = {
43    { PIN_ALGO_VERSION_V0, { SCRYPT_N_V0, SCRYPT_R, SCRYPT_P } },
44    { PIN_ALGO_VERSION_V1, { SCRYPT_N_V1, SCRYPT_R, SCRYPT_P } },
45    { PIN_ALGO_VERSION_V2, { SCRYPT_N_V1, SCRYPT_R, SCRYPT_P } },
46    { PIN_ALGO_VERSION_V3, { SCRYPT_N_V1, SCRYPT_R, SCRYPT_P_V3 } }
47};
48}
49
50bool Scrypt::DoScrypt(const std::vector<uint8_t> &data, uint32_t algoVersion, EVP_PKEY_CTX *pctx)
51{
52    auto index = g_version2Param_.find(algoVersion);
53    if (index == g_version2Param_.end()) {
54        IAM_LOGE("version is not in g_version2Param_");
55        return false;
56    }
57    ScryptParameters scryptParameters = index->second;
58    if (EVP_PKEY_CTX_set1_pbe_pass(pctx, reinterpret_cast<const char *>(data.data()), data.size()) <= 0) {
59        IAM_LOGE("EVP_PKEY_CTX_set1_pbe_pass fail");
60        return false;
61    }
62    if (EVP_PKEY_CTX_set1_scrypt_salt(pctx, algoParameter_.data(), algoParameter_.size()) <= 0) {
63        IAM_LOGE("EVP_PKEY_CTX_set1_scrypt_salt fail");
64        return false;
65    }
66    if (EVP_PKEY_CTX_set_scrypt_N(pctx, scryptParameters.scryptN) <= 0) {
67        IAM_LOGE("EVP_PKEY_CTX_set_scrypt_N fail");
68        return false;
69    }
70    if (EVP_PKEY_CTX_set_scrypt_r(pctx, scryptParameters.scryptR) <= 0) {
71        IAM_LOGE("EVP_PKEY_CTX_set_scrypt_r fail");
72        return false;
73    }
74    if (EVP_PKEY_CTX_set_scrypt_p(pctx, scryptParameters.scryptP) <= 0) {
75        IAM_LOGE("EVP_PKEY_CTX_set_scrypt_p fail");
76        return false;
77    }
78
79    return true;
80}
81
82std::vector<uint8_t> Scrypt::GetScrypt(const std::vector<uint8_t> &data, uint32_t algoVersion)
83{
84    IAM_LOGI("start");
85    std::vector<uint8_t> out;
86    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SCRYPT, NULL);
87    if (EVP_PKEY_derive_init(pctx) <= 0) {
88        IAM_LOGE("EVP_PKEY_derive_init fail");
89        return out;
90    }
91
92    if (!DoScrypt(data, algoVersion, pctx)) {
93        IAM_LOGE("DoScrypt fail");
94        EVP_PKEY_CTX_free(pctx);
95        return out;
96    }
97    out.resize(OUT_LENGTH);
98    size_t outlen = out.size();
99    if (EVP_PKEY_derive(pctx, out.data(), &outlen) <= 0) {
100        IAM_LOGE("EVP_PKEY_derive fail");
101        EVP_PKEY_CTX_free(pctx);
102        (void)memset_s(out.data(), out.size(), 0, out.size());
103        out.clear();
104        return out;
105    }
106
107    EVP_PKEY_CTX_free(pctx);
108    return out;
109}
110} // namespace PinAuth
111} // namespace UserIam
112} // namespace OHOS