openhitls/0007-provider-document.patch

967 lines
40 KiB
Diff
Raw Permalink Normal View History

From e8a6c4a6fce7409cd48433ed6441e000a4d0c5c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A6=BB=E6=A0=87=E9=87=8F?= <875922464@qq.com>
Date: Fri, 6 Dec 2024 17:00:59 +0800
Subject: [PATCH 07/10] provider document
Cherry-picked from: https://gitcode.com/openHiTLS/openhitls/merge_requests/92
---
.../4_provider Development Guide.md | 463 ++++++++++++++++++
docs/index/index.md | 5 +-
...00\345\217\221\346\214\207\345\215\227.md" | 458 +++++++++++++++++
3 files changed, 924 insertions(+), 2 deletions(-)
create mode 100644 docs/en/5_Developer Guide/4_provider Development Guide.md
create mode 100644 "docs/zh/5_\345\274\200\345\217\221\346\214\207\345\215\227/4_provider\345\274\200\345\217\221\346\214\207\345\215\227.md"
diff --git a/docs/en/5_Developer Guide/4_provider Development Guide.md b/docs/en/5_Developer Guide/4_provider Development Guide.md
new file mode 100644
index 0000000..f750a4d
--- /dev/null
+++ b/docs/en/5_Developer Guide/4_provider Development Guide.md
@@ -0,0 +1,463 @@
+# Provider Development Guide
+
+This document serves as a development guide for OpenHiTLS providers, providing developers with interface introductions, key feature descriptions, and comprehensive usage examples.
+
+## 1. Overview
+
+The provider management framework in OpenHiTLS supports dynamic loading, management, and usage of cryptographic providers. Each "Provider" encapsulates a specific set of cryptographic operations and exposes them to external users through standardized interfaces.
+
+### Core Concepts:
+- **Library Context (`CRYPT_EAL_LibCtx`)**: Manages the lifecycle and resources of all loaded providers.
+- **Provider Manager Context (`CRYPT_EAL_ProvMgrCtx`)**: Represents a single provider, including its loaded library handle and implemented functionalities.
+- **Functional Interfaces**: Standardized functions used for querying and invoking specific operations of providers.
+
+---
+
+## 2. Interface Introduction
+
+### 2.1 Library Context Management
+
+#### **`CRYPT_EAL_LibCtxNew`**
+- **Description**: Creates a new library context for managing providers.
+- **Function Prototype**:
+ ```c
+ CRYPT_EAL_LibCtx *CRYPT_EAL_LibCtxNew(void);
+ ```
+- **Return Value**: A pointer to the newly created library context.
+
+#### **`CRYPT_EAL_LibCtxFree`**
+- **Description**: Frees the library context and releases all associated resources.
+- **Function Prototype**:
+ ```c
+ void CRYPT_EAL_LibCtxFree(CRYPT_EAL_LibCtx *libCtx);
+ ```
+- **Parameters**:
+ - `libCtx`: The library context to be freed.
+
+---
+
+### 2.2 Path Configuration
+
+#### **`CRYPT_EAL_ProviderSetLoadPath`**
+- **Description**: Configures the path for loading providers.
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderSetLoadPath(
+ CRYPT_EAL_LibCtx *libCtx,
+ const char *searchPath
+ );
+ ```
+- **Parameters**:
+ - `libCtx`: The library context.
+ - `searchPath`: The search path for providers.
+- **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+
+---
+
+### 2.3 Provider Loading and Unloading
+
+#### **`CRYPT_EAL_ProviderLoad`**
+- **Description**: Dynamically loads a provider and initializes it.
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderLoad(
+ CRYPT_EAL_LibCtx *libCtx,
+ BSL_SAL_ConverterCmd cmd,
+ const char *providerName,
+ BSL_Param *param,
+ CRYPT_EAL_ProvMgrCtx **mgrCtx
+ );
+ ```
+- **Parameters**:
+ - `libCtx`: The library context.
+ - `cmd`: The command specifying the library format (e.g., `.so`, `lib*.so`).
+ - `providerName`: The name of the provider to load.
+ - `param`: Additional parameters for provider initialization.
+ - `mgrCtx`: Output pointer to the provider manager context. If not `NULL`, the manager context of the loaded provider will be returned upon successful loading.
+- **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+
+#### **`CRYPT_EAL_ProviderUnload`**
+- **Description**: Unloads the specified provider and releases associated resources.
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderUnload(
+ CRYPT_EAL_LibCtx *libCtx,
+ BSL_SAL_ConverterCmd cmd,
+ const char *providerName
+ );
+ ```
+- **Parameters**:
+ - `libCtx`: The library context.
+ - `cmd`: The command specifying the library format.
+ - `providerName`: The name of the provider to unload.
+- **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+
+---
+
+### 2.4 Algorithm Query and Invocation
+
+**EAL Layer Wrapper Interfaces**:
+
+These interfaces wrap the provider's exposed APIs, automatically initializing algorithms after querying suitable algorithms. See the corresponding headers for each algorithm for more details.
+
+- **Symmetric Interface: `CRYPT_EAL_ProviderCipherNewCtx`**
+- **Asymmetric Interface: `CRYPT_EAL_ProviderPkeyNewCtx`**
+- **KDF Interface: `CRYPT_EAL_ProviderKdfNewCtx`**
+- **MAC Interface: `CRYPT_EAL_ProviderMacNewCtx`**
+- **Message Digest Interface: `CRYPT_EAL_ProviderMdNewCtx`**
+- **Random Number Interfaces: `CRYPT_EAL_ProviderRandInitCtx`, `CRYPT_EAL_ProviderDrbgInitCtx`**
+
+**Provider Layer Exposed Interfaces**:
+
+#### **`CRYPT_EAL_ProviderGetFuncs`**
+- **Description**: Queries algorithms matching the criteria from all loaded providers.
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderGetFuncs(
+ CRYPT_EAL_LibCtx *libCtx,
+ int32_t operaId,
+ int32_t algId,
+ const char *attribute,
+ const CRYPT_EAL_Func **funcs,
+ void **provCtx
+ );
+ ```
+- **Parameters**:
+ - `libCtx`: The library context.
+ - `operaId`: Algorithm category ID (see "crypt_eal_implprovider.h").
+ - `algId`: Algorithm ID (see "crypt_eal_implprovider.h").
+ - `attribute`: Attribute string for filtering providers.
+ - `funcs`: Output pointer to an array of algorithms.
+ - `provCtx`: Optional parameter. If not `NULL`, it retrieves the `provCtx` from the provider manager context where the algorithm resides.
+- **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+
+#### **`CRYPT_EAL_ProviderCtrl`**
+- **Description**: Controls the `provCtx` in the provider manager context.
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderCtrl(
+ CRYPT_EAL_ProvMgrCtx *ctx,
+ int32_t cmd,
+ void *val,
+ uint32_t valLen
+ );
+ ```
+- **Parameters**:
+ - `ctx`: Provider manager context.
+ - `cmd`: Control command.
+ - `val`: The value associated with the command.
+ - `valLen`: The length of the value.
+
+---
+
+## 3. Provider Management Module Usage Instructions
+
+### 3.1 Loading and Unloading
+
+- **Feature Descriptions**:
+ - Providers are uniquely identified by their names. Different providers must have unique names. Providers with the same name but located in different paths are treated as the same provider.
+ - The framework supports repeated loading and unloading of providers. Repeated loading does not create additional provider manager contexts. To remove a provider manager context from the library context, the number of unloads must match the number of loads.
+ - When releasing the library context, all loaded providers are automatically unloaded.
+ - The default provider loading path is empty. If no path is set, the framework searches for providers in various locations based on the runtime environment and the behavior of the `dlopen` function.
+ - Currently, OpenHiTLS's built-in algorithm library is loaded into a globally initialized library context during startup. If `libCtx` is `NULL` during provider loading, unloading, or searching, this global library context is used.
+
+- **Usage Example**:
+ ```c
+ ...
+ // Create a library context
+ CRYPT_EAL_LibCtx *libCtx = CRYPT_EAL_LibCtxNew();
+ assert(libCtx != NULL);
+
+ // Set the provider loading path
+ int ret = CRYPT_EAL_ProviderSetLoadPath(libCtx, "/path/to/providers");
+ assert(ret == CRYPT_SUCCESS);
+
+ // Load a provider
+ CRYPT_EAL_ProvMgrCtx *mgrCtx = NULL;
+ ret = CRYPT_EAL_ProviderLoad(libCtx, BSL_SAL_CONVERTER_SO, "provider_name", NULL, &mgrCtx);
+ assert(ret == CRYPT_SUCCESS);
+
+ ...
+
+ // Unload the provider
+ ret = CRYPT_EAL_ProviderUnload(libCtx, BSL_SAL_CONVERTER_SO, "provider_name");
+ assert(ret == CRYPT_SUCCESS);
+
+ // Free the library context
+ CRYPT_EAL_LibCtxFree(libCtx);
+ ...
+ ```
+
+---
+
+### 3.2 Attribute Query and Provider Scoring Mechanism
+
+- **Attribute Mechanism**:
+ When querying algorithms, the framework first searches for algorithms matching the algorithm ID. If the search string is not `NULL`, it further selects the best-matching algorithm from all loaded providers based on the search string.
+ Provider algorithm attributes are composed of a `name` and a `value`, separated by `=`. Multiple attributes are separated by `,`. Within a provider, each algorithm can define one or more sets of attributes based on its implementation purpose. Even the same algorithm can have different implementations distinguished by attributes.
+
+- **Provider Scoring Mechanism**:
+ Queries can consist of multiple query statements, separated by `,`. Within a statement, `name` and `value` are separated by the following **comparison characters**:
+ - `=`: Must be equal (mandatory condition).
+ - `!=`: Must not be equal (mandatory condition).
+ - `?`: Optional condition. If `value` matches, it is prioritized. Each match adds +1 to the score.
+
+ Mandatory conditions must be satisfied. Among providers that meet the mandatory conditions, the framework selects the best match based on optional conditions. If there are multiple best matches, one is randomly selected.
+ Repeated conditions are allowed within query statements:
+ - Repeated mandatory conditions have no effect.
+ - Repeated optional conditions increase the score proportionally.
+
+- **Usage Example**:
+ ```c
+ ...
+ // Query with a NULL attribute string, searching by algorithm ID
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, NULL, &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // Query with a non-NULL attribute string, matching based on rules
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, "name=md5,type=hash,version=1.0", &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // Query with provider scoring mechanism
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, "name=md5,feature?attr_good,feature?attr_good,feature?attr_bad", &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // For actual use, it is recommended to use EAL layer wrapping interfaces for each algorithm,
+ // which automatically locate and initialize the algorithm.
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(libCtx, CRYPT_MD_MD5, "provider=no_hitls,type=hash");
+ assert(ctx != NULL);
+ ...
+ ```
+
+---
+
+## 4. Provider Construction Instructions
+
+This section describes the commands and function prototypes, which are fully defined in the `crypt_eal_implprovider.h` header file.
+
+### 4.1 Initialization Function
+
+For each provider, an initialization function named `CRYPT_EAL_ProviderInit` must be implemented. This function is called when loading the provider:
+- **Function Prototype**:
+ ```c
+ int32_t CRYPT_EAL_ProviderInit(
+ CRYPT_EAL_ProvMgrCtx *mgrCtx,
+ BSL_Param *param,
+ CRYPT_EAL_Func *capFuncs,
+ CRYPT_EAL_Func **outFuncs,
+ void **provCtx
+ );
+ ```
+ - **Parameters**:
+ - `mgrCtx`: [in] Provider manager context.
+ - `param`: [in] Additional parameters for provider initialization.
+ - `capFuncs`: [in] Algorithm array pointer provided by the management framework, allowing users to optionally use HITLS's default entropy source.
+ - `outFuncs`: [out] Array pointer for algorithms exposed by the provider. Details are described below.
+ - `provCtx`: [out] Provider-specific structure. If needed, private data can be saved in the provider manager context. Details on its operation are below (optional).
+ - **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+
+- **Description of the `outFuncs` Array**:
+ This array is used to pass the three types of algorithm arrays provided by the provider. **The algorithm query function must be returned**, while the other two functions are optional:
+ - **Algorithm Query Function**: Used to retrieve the algorithm array provided by the provider based on the algorithm category during a search:
+ **`typedef int32_t (*)(void *provCtx, int32_t operaId, CRYPT_EAL_AlgInfo **algInfos);`**
+ - **Parameters**:
+ - `provCtx`: [in] Provider-specific structure (optional).
+ - `operaId`: [in] Algorithm category ID.
+ - `algInfos`: [out] Pointer to the array of all algorithms under the category. The array ends when the algorithm ID is 0.
+ - **Return Value**: Returns `CRYPT_SUCCESS` on success, otherwise an error code.
+ - **`provCtx` Control Function**: If the provider uses `provCtx`, this function is used for its control. It can be invoked using the `CRYPT_EAL_ProviderCtrl` function.
+ **`typedef int32_t (*)(void *provCtx, int32_t cmd, void *val, uint32_t valLen);`**
+ - **Parameters**: Omitted.
+ - **Return Value**: Omitted.
+ - **`provCtx` Release Function**: If the provider uses `provCtx`, this function releases `provCtx`. It is called during resource release.
+ **`typedef void (*)(void *provCtx);`**
+ - **Parameters**:
+ - `provCtx`: [in] Provider-specific structure.
+ - **Return Value**: None.
+
+---
+
+### 4.2 Provider Construction Example
+
+- **Initialization Function Example**:
+```c
+static CRYPT_EAL_Func defProvOutFuncs[] = {
+ {CRYPT_EAL_PROVCB_QUERY, CRYPT_EAL_DefaultProvQuery},
+ {CRYPT_EAL_PROVCB_FREE, NULL},
+ {CRYPT_EAL_PROVCB_CTRL, NULL},
+ CRYPT_EAL_FUNC_END
+};
+
+int32_t CRYPT_EAL_ProviderInit(CRYPT_EAL_ProvMgrCtx *mgrCtx,
+ BSL_Param *param, CRYPT_EAL_Func *capFuncs, CRYPT_EAL_Func **outFuncs, void **provCtx)
+{
+ CRYPT_RandSeedMethod entroy = {0};
+ CRYPT_EAL_ProvMgrCtrlCb mgrCtrl = NULL;
+ int32_t index = 0;
+ while (capFuncs[index].id != 0) {
+ switch (capFuncs[index].id) {
+ case CRYPT_EAL_CAP_GETENTROPY:
+ entroy.getEntropy = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_CLEANENTROPY:
+ entroy.cleanEntropy = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_GETNONCE:
+ entroy.getNonce = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_CLEANNONCE:
+ entroy.cleanNonce = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_MGRCTXCTRL:
+ mgrCtrl = capFuncs[index].func;
+ break;
+ default:
+ return CRYPT_PROVIDER_ERR_UNEXPECTED_IMPL;
+ }
+ index++;
+ }
+ void *seedCtx = NULL;
+ void *libCtx = NULL;
+ if (entroy.getEntropy == NULL || entroy.cleanEntropy == NULL || entroy.getNonce == NULL ||
+ entroy.cleanNonce == NULL || mgrCtrl == NULL) {
+ return CRYPT_NULL_INPUT;
+ }
+ int32_t ret = mgrCtrl(mgrCtx, CRYPT_EAL_MGR_GETSEEDCTX, &seedCtx, 0);
+ if (ret != CRYPT_SUCCESS) {
+ return ret;
+ }
+ ret = mgrCtrl(mgrCtx, CRYPT_EAL_MGR_GETLIBCTX, &libCtx, 0);
+ if (ret != CRYPT_SUCCESS) {
+ return ret;
+ }
+ CRYPT_Data entropy = {NULL, 0};
+ CRYPT_Range entropyRange = {32, 2147483632};
+ ret = entroy.getEntropy(seedCtx, &entropy, 256, &entropyRange);
+ if (ret != CRYPT_SUCCESS) {
+ return CRYPT_DRBG_FAIL_GET_ENTROPY;
+ }
+ entroy.cleanEntropy(seedCtx, &entropy);
+ // check libCtx
+ if (param != NULL) {
+ if (param[0].value != libCtx) {
+ return CRYPT_INVALID_ARG;
+ }
+ }
+ *outFuncs = defProvOutFuncs;
+ return 0;
+}
+```
+- **Algorithm query function example**:
+```c
+const CRYPT_EAL_Func defMdMd5[] = {
+ {CRYPT_EAL_IMPLMD_NEWCTX, ...},
+ {CRYPT_EAL_IMPLMD_INITCTX, ...},
+ {CRYPT_EAL_IMPLMD_UPDATE, ...},
+ {CRYPT_EAL_IMPLMD_FINAL, ...},
+ {CRYPT_EAL_IMPLMD_DEINITCTX, ...},
+ {CRYPT_EAL_IMPLMD_DUPCTX, ...},
+ {CRYPT_EAL_IMPLMD_CTRL, ...},
+ {CRYPT_EAL_IMPLMD_FREECTX, ...},
+ CRYPT_EAL_FUNC_END,
+};
+
+static const CRYPT_EAL_AlgInfo defMds[] = {
+ ...
+ {CRYPT_MD_MD5, defMdMd5, "attr1=temp_attr1,attr2=temp_attr2"},
+ ...
+ CRYPT_EAL_ALGINFO_END
+};
+
+static int32_t CRYPT_EAL_DefaultProvQuery(void *provCtx, int32_t operaId, const CRYPT_EAL_AlgInfo **algInfos)
+{
+ (void) provCtx;
+ int32_t ret = CRYPT_SUCCESS;
+ switch (operaId) {
+ ...
+ case CRYPT_EAL_OPERAID_HASH:
+ *algInfos = defMds;
+ break;
+ ...
+ default:
+ ret = CRYPT_NOT_SUPPORT;
+ break;
+ }
+ return ret;
+}
+```
+
+---
+
+## 5. Comprehensive Usage Example
+
+```c
+#include "bsl_sal.h"
+#include "crypt_eal_provider.h"
+#include "crypt_eal_implprovider.h"
+#include "crypt_eal_md.h"
+
+/* Using the SM3 algorithm */
+
+int main() {
+
+------------------------------------------------------------------------------------------------------
+
+// Using the built-in HITLS algorithm library:
+ // Step 1: Directly initialize using EAL layer MD interface
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(NULL, CRYPT_MD_SM3, "provider=default");
+ ASSERT_TRUE(ctx != NULL);
+
+-----------------------------------------------
+
+// Searching and initializing with a matching algorithm library in a third-party provider:
+ // Step 1: Create a library context
+ CRYPT_EAL_LibCtx *libCtx = CRYPT_EAL_LibCtxNew();
+ ASSERT_TRUE(libCtx != NULL);
+
+ // Step 2: Set the provider loading path
+ int ret = CRYPT_EAL_ProviderSetLoadPath(libCtx, "/path/to/providers");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // Step 3: Load the provider
+ ret = CRYPT_EAL_ProviderLoad(libCtx, BSL_SAL_LIB_FMT_SO, "provider_name", NULL, NULL);
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // Step 4: Directly initialize using EAL layer MD interface
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(libCtx, CRYPT_MD_SM3, "attr1=temp_attr1,attr2=temp_attr2");
+ ASSERT_TRUE(ctx != NULL);
+
+-----------------------------------------------
+
+// Mixed usage of third-party providers and HITLS libraries:
+ // Step 1: Set the provider loading path
+ int ret = CRYPT_EAL_ProviderSetLoadPath(NULL, "/path/to/providers");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // Step 2: Load the provider
+ ret = CRYPT_EAL_ProviderLoad(NULL, BSL_SAL_LIB_FMT_SO, "provider_name", NULL, NULL);
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // Step 3: Directly initialize using EAL layer MD interface
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(NULL, CRYPT_MD_SM3, "attr1=temp_attr1,attr2=temp_attr2");
+ ASSERT_TRUE(ctx != NULL);
+
+------------------------------------------------------------------------------------------------------
+
+// After initialization, a series of algorithm operations can be performed:
+ ASSERT_EQ(CRYPT_EAL_MdInit(ctx), CRYPT_SUCCESS);
+ ASSERT_EQ(CRYPT_EAL_MdUpdate(ctx, msg->x, msg->len), CRYPT_SUCCESS);
+ ASSERT_EQ(CRYPT_EAL_MdFinal(ctx, output, &outLen), CRYPT_SUCCESS);
+
+------------------------------------------------------------------------------------------------------
+
+// If a library context was created or a third-party provider was loaded during initialization, they need to be released:
+ // Unload the provider
+ ret = CRYPT_EAL_ProviderUnload(libCtx, BSL_SAL_CONVERTER_SO, "provider_name");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // Free the library context
+ CRYPT_EAL_LibCtxFree(libCtx);
+
+ return 0;
+}
+```
\ No newline at end of file
diff --git a/docs/index/index.md b/docs/index/index.md
index 28ea2ae..412be81 100644
--- a/docs/index/index.md
+++ b/docs/index/index.md
@@ -11,6 +11,7 @@
| 7 | [加密完保应用开发指南](../zh/5_开发指南/1_加密完保应用开发指南.md) | [Encryption and Integrity Protection Application Development Guide](../en/5_Developer%20Guide/1_Encryption%20and%20Integrity%20Protection%20Application%20Development%20Guide.md) |
| 8 | [安全通信应用开发指南](../zh/5_开发指南/2_安全通信应用开发指南.md) | [Secure Communication Application Development Guide](../en/5_Developer%20Guide/2_Secure%20Communication%20Application%20Development%20Guide.md) |
| 9 | [API参考](../zh/5_开发指南/3_API参考.md) | [API Reference](../en/5_Developer%20Guide/3_API%20Reference.md) |
-| 10 | [术语列表](../zh/6_附录/1_术语列表.md) | [Terms](../en/6_Appendix/1_Terms.md) |
-| 11 | [修订记录](../zh/6_附录/2_修订记录.md) | [Change History](../en/6_Appendix/2_Change%20History.md) |
+| 10 | [provider开发指南](../zh/5_开发指南/4_provider开发指南.md) | [provider Development Guide](../en/5_Developer%20Guide/4_provider%20Development%20Guide.md) |
+| 11 | [术语列表](../zh/6_附录/1_术语列表.md) | [Terms](../en/6_Appendix/1_Terms.md) |
+| 12 | [修订记录](../zh/6_附录/2_修订记录.md) | [Change History](../en/6_Appendix/2_Change%20History.md) |
diff --git "a/docs/zh/5_\345\274\200\345\217\221\346\214\207\345\215\227/4_provider\345\274\200\345\217\221\346\214\207\345\215\227.md" "b/docs/zh/5_\345\274\200\345\217\221\346\214\207\345\215\227/4_provider\345\274\200\345\217\221\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..4ae2b56
--- /dev/null
+++ "b/docs/zh/5_\345\274\200\345\217\221\346\214\207\345\215\227/4_provider\345\274\200\345\217\221\346\214\207\345\215\227.md"
@@ -0,0 +1,458 @@
+# Provider 开发指南
+
+本文档是 OpenHiTLS Provider 的开发指南,为开发人员提供接口介绍、关键特性说明以及综合使用示例。
+
+## 1. 概述
+
+OpenHiTLS 的 Provider 管理框架支持动态加载、管理和使用加密 Provider。每个 "Provider" 封装了一组特定的加密操作,通过标准化接口向外部用户暴露。
+
+### 核心概念:
+- **库上下文Library Context`CRYPT_EAL_LibCtx`**:用于管理所有加载的 Provider 的生命周期和资源。
+- **Provider 管理上下文Provider Manager Context`CRYPT_EAL_ProvMgrCtx`**:表示单个 Provider包括其加载库句柄和功能实现。
+- **功能接口Functional Interfaces**:标准化的函数,用于查询和调用 Provider 的具体操作。
+
+---
+
+## 2. 接口介绍
+
+### 2.1 库上下文管理
+
+#### **`CRYPT_EAL_LibCtxNew`**
+- **描述**:创建一个新的库上下文,用于管理 Provider。
+- **函数原型**
+ ```c
+ CRYPT_EAL_LibCtx *CRYPT_EAL_LibCtxNew(void);
+ ```
+- **返回值**:指向新创建的库上下文的指针。
+
+#### **`CRYPT_EAL_LibCtxFree`**
+- **描述**:释放库上下文,并释放所有相关资源。
+- **函数原型**
+ ```c
+ void CRYPT_EAL_LibCtxFree(CRYPT_EAL_LibCtx *libCtx);
+ ```
+- **参数**
+ - `libCtx`:需要释放的库上下文。
+
+---
+
+### 2.2 路径配置
+
+#### **`CRYPT_EAL_ProviderSetLoadPath`**
+- **描述**:配置加载 Provider 的路径。
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderSetLoadPath(
+ CRYPT_EAL_LibCtx *libCtx,
+ const char *searchPath
+ );
+ ```
+- **参数**
+ - `libCtx`:库上下文。
+ - `searchPath`Provider 的搜索路径。
+- **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+
+---
+
+### 2.3 Provider 加载与卸载
+
+#### **`CRYPT_EAL_ProviderLoad`**
+- **描述**:动态加载一个 Provider并完成初始化。
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderLoad(
+ CRYPT_EAL_LibCtx *libCtx,
+ BSL_SAL_ConverterCmd cmd,
+ const char *providerName,
+ BSL_Param *param,
+ CRYPT_EAL_ProvMgrCtx **mgrCtx
+ );
+ ```
+- **参数**
+ - `libCtx`:库上下文。
+ - `cmd`:指定库格式的命令(例如 `.so` 或 `lib*.so`)。
+ - `providerName`:要加载的 Provider 名称。
+ - `param`:初始化 Provider 时的附加参数。
+ - `mgrCtx`:输出的 Provider 管理上下文指针如果不为NULL那么将会在加载成功后返回对应provider的管理上下文CRYPT_EAL_ProvMgrCtx
+- **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+
+#### **`CRYPT_EAL_ProviderUnload`**
+- **描述**:卸载指定的 Provider并释放相关资源。
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderUnload(
+ CRYPT_EAL_LibCtx *libCtx,
+ BSL_SAL_ConverterCmd cmd,
+ const char *providerName
+ );
+ ```
+- **参数**
+ - `libCtx`:库上下文。
+ - `cmd`:指定库格式的命令。
+ - `providerName`:要卸载的 Provider 名称。
+- **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+
+---
+
+### 2.4 算法查询与调用
+
+**eal层对外包装接口**
+
+该部分接口会包装provider对外接口在查询到适配的算法后自动调用算法进行初始化工作详见各类算法的对外头文件。
+
+- **对称接口:`CRYPT_EAL_ProviderCipherNewCtx`**
+- **非对称接口:`CRYPT_EAL_ProviderPkeyNewCtx`**
+- **kdf接口`CRYPT_EAL_ProviderKdfNewCtx`**
+- **mac接口`CRYPT_EAL_ProviderMacNewCtx`**
+- **md接口`CRYPT_EAL_ProviderMdNewCtx`**
+- **随机数接口:`CRYPT_EAL_ProviderRandInitCtx`、`CRYPT_EAL_ProviderDrbgInitCtx`**
+
+
+**provider底层对外接口**
+
+#### **`CRYPT_EAL_ProviderGetFuncs`**
+- **描述**从所有加载的provider中查询符合要求的算法。
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderGetFuncs(
+ CRYPT_EAL_LibCtx *libCtx,
+ int32_t operaId,
+ int32_t algId,
+ const char *attribute,
+ const CRYPT_EAL_Func **funcs,
+ void **provCtx
+ );
+ ```
+- **参数**
+ - `libCtx`:库上下文。
+ - `operaId`:算法类别 ID详见”crypt_eal_implprovider.h“文件
+ - `algId`:算法 ID 详见”crypt_eal_implprovider.h“文件
+ - `attribute`:用于筛选 Provider 的属性字符串。
+ - `funcs`:输出的算法数组指针。
+ - `provCtx`可选参数如果不为NULL会获得该算法所在provider管理上下文中的provCtx。
+- **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+
+#### **`CRYPT_EAL_ProviderCtrl`**
+- **描述**:控制 Provider 管理上下文中的provCtx。
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderCtrl(
+ CRYPT_EAL_ProvMgrCtx *ctx,
+ int32_t cmd,
+ void *val,
+ uint32_t valLen
+ );
+ ```
+- **参数**
+ - `ctx`Provider 管理上下文。
+ - `cmd`:控制命令。
+ - `val`:与命令相关的值。
+ - `valLen`:值的长度。
+
+---
+
+## 3. provider管理模块使用说明
+
+### 3.1 加载与卸载
+
+- **特性说明**
+ - provider以名称为唯一标识符不同的provider要求具有不同的名称不同路径相同名称的provider会被视为相同的provider。
+ - 支持重复加载和卸载provider重复加载时并不会额外创建provider管理上下文卸载的次数需要与加载的次数相同才能将库上下文中的provider管理上下文删除。
+ - 在释放库上下文时会自动卸载所有加载的provider。
+ - 加载provider的路径默认为空如果没有设置加载路径那么将会根据运行环境中dlopen函数的当前特性从各个位置依次检索搜索provider。
+ - 目前openhitls自带的算法库会被加载进一个启动时初始化的全局库上下文中当加载、卸载以及查找provider时如果传入的libCtx为NULL那么会使用该全局库上下文。
+- **使用示例**
+ ```c
+ ...
+ // 创建库上下文
+ CRYPT_EAL_LibCtx *libCtx = CRYPT_EAL_LibCtxNew();
+ assert(libCtx != NULL);
+
+ // 设置 Provider 加载路径
+ int ret = CRYPT_EAL_ProviderSetLoadPath(libCtx, "/path/to/providers");
+ assert(ret == CRYPT_SUCCESS);
+
+ // 加载 Provider
+ CRYPT_EAL_ProvMgrCtx *mgrCtx = NULL;
+ ret = CRYPT_EAL_ProviderLoad(libCtx, BSL_SAL_CONVERTER_SO, "provider_name", NULL, &mgrCtx);
+ assert(ret == CRYPT_SUCCESS);
+
+ ...
+
+ // 卸载 Provider
+ ret = CRYPT_EAL_ProviderUnload(libCtx, BSL_SAL_CONVERTER_SO, "provider_name");
+ assert(ret == CRYPT_SUCCESS);
+
+ // 释放库上下文
+ CRYPT_EAL_LibCtxFree(libCtx);
+ ...
+ ```
+
+---
+
+### 3.2 属性查询和provider打分机制
+
+- **属性机制**
+ 在查询算法时首先会查找符合算法ID的算法数组如果用户输入的查找字符串不为NULL则还会根据查找字符串选择所有加载provider中最匹配的算法。
+ 供查找的provider算法属性值由name和value组成name和value之间用`=`分隔,多组属性之间用`,`分隔。在provider中每个算法可以根据实现目的定义一组或多组属性甚至同一个算法可以有不同实现通过属性区分
+- **provider打分机制**
+ 查询可以由多个查询语句组成,每个语句中之间用`,`分隔语句中的name和value之间用以下`判断字符`分隔:
+ - `=`: 必须相等,属于强制条件。
+ - `!=`: 必须不相等,属于强制条件。
+ - ``: 可选条件如果value匹配优先选择每满足一个问号得分会+1。
+
+ 查询时强制条件必须满足,在此基础上,根据可选条件选择最匹配的,如果最匹配实现有多个选择,随机返回一个。
+ 查询时允许各组语句重复强制条件重复对结果无影响。可选条件重复相当于满足该条件的得分由1分变为重复个数的分数。
+- **使用示例**
+ ```c
+ ...
+ // 属性字符串可以为NILL会根据算法ID查找
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, NULL, &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // 属性字符串不为NULL时根据匹配规则查找
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, "name=md5,type=hash,version=1.0", &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // 属性字符串涉及provider打分机制
+ ret = CRYPT_EAL_ProviderGetFuncs(libCtx, CRYPT_EAL_OPERAID_HASH, CRYPT_MD_MD5, "name=md5,feature?attr_good,feature?attr_good,feature?attr_bad", &funcs, &provCtx);
+ assert(ret == CRYPT_SUCCESS);
+ ...
+ // 实际使用时推荐使用eal层各类算法对外的包装接口,会自动进行查找算法并调用算法进行初始化等工作。
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(libCtx, CRYPT_MD_MD5, "provider=no_hitls,type=hash");
+ assert(ctx != NULL);
+ ...
+ ```
+
+---
+
+## 4. provider构建说明
+
+该部分涉及的各类命令以及函数原型的完整内容定义在`crypt_eal_implprovider.h`头文件中。
+
+### 4.1 初始化函数
+
+对每个provider需要实现名为`CRYPT_EAL_ProviderInit`的初始化函数该初始化函数会在加载provider时被调用
+- **函数原型**
+ ```c
+ int32_t CRYPT_EAL_ProviderInit(
+ CRYPT_EAL_ProvMgrCtx *mgrCtx,
+ BSL_Param *param,
+ CRYPT_EAL_Func *capFuncs,
+ CRYPT_EAL_Func **outFuncs,
+ void **provCtx
+ );
+ ```
+ - **参数**
+ - `mgrCtx`[in] Provider 管理上下文。
+ - `param`[in] 初始化 Provider 时的附加参数。
+ - `capFuncs`[in] 由管理框架输入的算法数组指针目前支持让用户可以选择使用HITLS提供的默认熵源。
+ - `outFuncs`[out] provider对外提供的算法数组指针详情见下文。
+ - `provCtx`[out] provider私有结构体如需要可用于在provider管理上下文中保存一些私有数据操作详情见下文可选。
+ - **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+
+- **`outFuncs`数组说明**
+该数组用于传出provider对外提供的三种算法数组其中**算法查询函数必须返回**,其他两个函数可选:
+ - **算法查询函数**该函数用于在查找算法时根据传入的算法类别获取provider对外提供的整个算法类别的方法数组:
+ **`typedef int32_t (*)(void *provCtx, int32_t operaId, CRYPT_EAL_AlgInfo **algInfos);`**
+ - **参数**
+ - `provCtx`[in] provider私有结构体可选。
+ - `operaId`[in] 算法类别ID。
+ - `algInfos`[out] 该算法类别下所有算法的数组指针数组中当算法ID为0时表示结束。
+ - **返回值**:成功返回 `CRYPT_SUCCESS`,否则返回错误代码。
+ - **provCtx控制函数**如果provider使用了provCtx,则该函数用于对其进行控制,该函数可以通过`CRYPT_EAL_ProviderCtrl`函数进行调用。
+ **`typedef int32_t (*)(void *provCtx, int32_t cmd, void *val, uint32_t valLen);`**
+ - **参数**:略
+ - **返回值**:略
+ - **provCtx释放函数**如果provider使用了provCtx,则该函数用于释放provCtx该函数会在释放资源时被调用。
+ **`typedef void (*)(void *provCtx);`**
+ - **参数**
+ - ·`provCtx`[in] provider私有结构体。
+ - **返回值**:无。
+
+---
+
+### 4.2 provider构建示例
+
+- **初始化函数示例**
+```c
+static CRYPT_EAL_Func defProvOutFuncs[] = {
+ {CRYPT_EAL_PROVCB_QUERY, CRYPT_EAL_DefaultProvQuery},
+ {CRYPT_EAL_PROVCB_FREE, NULL},
+ {CRYPT_EAL_PROVCB_CTRL, NULL},
+ CRYPT_EAL_FUNC_END
+};
+
+int32_t CRYPT_EAL_ProviderInit(CRYPT_EAL_ProvMgrCtx *mgrCtx,
+ BSL_Param *param, CRYPT_EAL_Func *capFuncs, CRYPT_EAL_Func **outFuncs, void **provCtx)
+{
+ CRYPT_RandSeedMethod entroy = {0};
+ CRYPT_EAL_ProvMgrCtrlCb mgrCtrl = NULL;
+ int32_t index = 0;
+ while (capFuncs[index].id != 0) {
+ switch (capFuncs[index].id) {
+ case CRYPT_EAL_CAP_GETENTROPY:
+ entroy.getEntropy = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_CLEANENTROPY:
+ entroy.cleanEntropy = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_GETNONCE:
+ entroy.getNonce = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_CLEANNONCE:
+ entroy.cleanNonce = capFuncs[index].func;
+ break;
+ case CRYPT_EAL_CAP_MGRCTXCTRL:
+ mgrCtrl = capFuncs[index].func;
+ break;
+ default:
+ return CRYPT_PROVIDER_ERR_UNEXPECTED_IMPL;
+ }
+ index++;
+ }
+ void *seedCtx = NULL;
+ void *libCtx = NULL;
+ if (entroy.getEntropy == NULL || entroy.cleanEntropy == NULL || entroy.getNonce == NULL ||
+ entroy.cleanNonce == NULL || mgrCtrl == NULL) {
+ return CRYPT_NULL_INPUT;
+ }
+ int32_t ret = mgrCtrl(mgrCtx, CRYPT_EAL_MGR_GETSEEDCTX, &seedCtx, 0);
+ if (ret != CRYPT_SUCCESS) {
+ return ret;
+ }
+ ret = mgrCtrl(mgrCtx, CRYPT_EAL_MGR_GETLIBCTX, &libCtx, 0);
+ if (ret != CRYPT_SUCCESS) {
+ return ret;
+ }
+ CRYPT_Data entropy = {NULL, 0};
+ CRYPT_Range entropyRange = {32, 2147483632};
+ ret = entroy.getEntropy(seedCtx, &entropy, 256, &entropyRange);
+ if (ret != CRYPT_SUCCESS) {
+ return CRYPT_DRBG_FAIL_GET_ENTROPY;
+ }
+ entroy.cleanEntropy(seedCtx, &entropy);
+ // check libCtx
+ if (param != NULL) {
+ if (param[0].value != libCtx) {
+ return CRYPT_INVALID_ARG;
+ }
+ }
+ *outFuncs = defProvOutFuncs;
+ return 0;
+}
+```
+- **算法查询函数示例**
+```c
+const CRYPT_EAL_Func defMdMd5[] = {
+ {CRYPT_EAL_IMPLMD_NEWCTX, ...},
+ {CRYPT_EAL_IMPLMD_INITCTX, ...},
+ {CRYPT_EAL_IMPLMD_UPDATE, ...},
+ {CRYPT_EAL_IMPLMD_FINAL, ...},
+ {CRYPT_EAL_IMPLMD_DEINITCTX, ...},
+ {CRYPT_EAL_IMPLMD_DUPCTX, ...},
+ {CRYPT_EAL_IMPLMD_CTRL, ...},
+ {CRYPT_EAL_IMPLMD_FREECTX, ...},
+ CRYPT_EAL_FUNC_END,
+};
+
+static const CRYPT_EAL_AlgInfo defMds[] = {
+ ...
+ {CRYPT_MD_MD5, defMdMd5, "attr1=temp_attr1,attr2=temp_attr2"},
+ ...
+ CRYPT_EAL_ALGINFO_END
+};
+
+static int32_t CRYPT_EAL_DefaultProvQuery(void *provCtx, int32_t operaId, const CRYPT_EAL_AlgInfo **algInfos)
+{
+ (void) provCtx;
+ int32_t ret = CRYPT_SUCCESS;
+ switch (operaId) {
+ ...
+ case CRYPT_EAL_OPERAID_HASH:
+ *algInfos = defMds;
+ break;
+ ...
+ default:
+ ret = CRYPT_NOT_SUPPORT;
+ break;
+ }
+ return ret;
+}
+```
+
+---
+
+## 5. 综合使用示例
+
+```c
+#include "bsl_sal.h"
+#include "crypt_eal_provider.h"
+#include "crypt_eal_implprovider.h"
+#include "crypt_eal_md.h"
+
+/* 调用SM3 算法 */
+
+int main() {
+
+------------------------------------------------------------------------------------------------------
+
+// 调用hitls自带的算法库初始化方式
+ // 步骤 1调用eal层的md对外接口直接进行初始化
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(NULL, CRYPT_MD_SM3, "provider=default");
+ ASSERT_TRUE(ctx != NULL);
+
+-----------------------------------------------
+
+// 在第三方provider中寻找匹配的算法库并初始化方式
+ // 步骤 1创建库上下文
+ CRYPT_EAL_LibCtx *libCtx = CRYPT_EAL_LibCtxNew();
+ ASSERT_TRUE(libCtx != NULL);
+
+ // 步骤 2设置 Provider 加载路径
+ int ret = CRYPT_EAL_ProviderSetLoadPath(libCtx, "/path/to/providers");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // 步骤 3加载 Provider
+ ret = CRYPT_EAL_ProviderLoad(libCtx, BSL_SAL_LIB_FMT_SO, "provider_name", NULL, NULL);
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // 步骤 4调用eal层的md对外接口直接进行初始化
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(libCtx, CRYPT_MD_SM3, "attr1=temp_attr1,attr2=temp_attr2");
+ ASSERT_TRUE(ctx != NULL);
+
+-----------------------------------------------
+
+// 第三方provider和hitls提供的算法库混合使用的场景寻找匹配的算法库并初始化方式
+ // 步骤 1设置 Provider 加载路径
+ int ret = CRYPT_EAL_ProviderSetLoadPath(NULL, "/path/to/providers");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // 步骤 2加载 Provider
+ ret = CRYPT_EAL_ProviderLoad(NULL, BSL_SAL_LIB_FMT_SO, "provider_name", NULL, NULL);
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // 步骤 3调用eal层的md对外接口直接进行初始化
+ CRYPT_EAL_MdCTX *ctx = CRYPT_EAL_ProviderMdNewCtx(NULL, CRYPT_MD_SM3, "attr1=temp_attr1,attr2=temp_attr2");
+ ASSERT_TRUE(ctx != NULL);
+
+------------------------------------------------------------------------------------------------------
+
+// 初始化后可进行一系列的算法操作:
+ ASSERT_EQ(CRYPT_EAL_MdInit(ctx), CRYPT_SUCCESS);
+ ASSERT_EQ(CRYPT_EAL_MdUpdate(ctx, msg->x, msg->len), CRYPT_SUCCESS);
+ ASSERT_EQ(CRYPT_EAL_MdFinal(ctx, output, &outLen), CRYPT_SUCCESS);
+
+------------------------------------------------------------------------------------------------------
+
+// 如果初始化时创建了库上下文或者加载了第三方provider,则需要进行释放
+ // 卸载 Provider
+ ret = CRYPT_EAL_ProviderUnload(libCtx, BSL_SAL_CONVERTER_SO, "provider_name");
+ ASSERT_TRUE(ret == CRYPT_SUCCESS);
+
+ // 释放库上下文
+ CRYPT_EAL_LibCtxFree(libCtx);
+
+ return 0;
+}
+```
\ No newline at end of file
--
2.42.0.windows.2