如何整合 openSSL custom provider (以 TRNG 舉例)

文章目录

    • 目標
    • [Provider interface for RNG (Random Number Generator)](#Provider interface for RNG (Random Number Generator))
    • [OSSL_DISPATCH to init provider](#OSSL_DISPATCH to init provider)
    • [RNG 類型介紹](#RNG 類型介紹)
    • [Dynamic loadable module and build-in module](#Dynamic loadable module and build-in module)
      • [Provider 如何使用 build-in module](#Provider 如何使用 build-in module)
      • [Provider 如何使用 dynamic loadable module](#Provider 如何使用 dynamic loadable module)
    • [Openssl RNG 入口函數](#Openssl RNG 入口函數)
    • FAQ
    • Reference

目標

提供方法將 SE TRNG(安全元件真隨機數生成器) 集成到 OpenSSL 加密框架中,通過 Provider 或 Engine 接口,利用SE的TRNG能力,實現安全、高效能的隨機數生成。

Provider interface for RNG (Random Number Generator)

The concept of providers and everything surrounding them was introduced in OpenSSL 3.0.

Provider 可以提供例如加密與解密、密鑰衍生、MAC 計算、簽署與驗證、隨機數產生器(Random Number Generator, RNG)等的替換,下文僅描述provider 有關RNG的介面。

A provider offers an initialization function, as a set of base functions in the form of an OSSL_DISPATCH array, and by extension, a set of OSSL_ALGORITHMs

OSSL_LIB_CTX *ctx (libopenssl 上下文)裡面的組成

  • OSSL_DISPATCH 是一個用來描述特定功能實作的結構體,提供功能ID與對應的函數指針。
    提供者通過一組 OSSL_DISPATCH 來註冊它所實作的功能,每個功能對應於一個 function_id 和其具體的函數實作。
c 复制代码
typedef struct ossl_dispatch_st OSSL_DISPATCH;
struct ossl_dispatch_st {
    int function_id;
    void (*function)(void);
};
  • OSSL_ALGORITHM 是一個用來描述提供者支持的演算法的結構體,它與 OSSL_DISPATCH 一起使用,將演算法名稱與功能對應。
    provider 宣告其支持的演算法名稱及對應的實作(由OSSL_DISPATCH array組成)。
c 复制代码
#include <openssl/core.h>

typedef struct ossl_algorithm_st OSSL_ALGORITHM;
struct ossl_algorithm_st {
    const char *algorithm_names;     /* key */
    const char *property_definition; /* openssl 定義一套搜尋方法property,當有多種演算法的時候可用於檢索 */
    const OSSL_DISPATCH *implementation;
    const char *algorithm_description; /* 簡短描述 */
};
Member Description Mandatory/Optional
algorithm_name The name of the algorithm (e.g., "AES-128-CBC", "SHA256", "RAND"). Mandatory
property_definition A property string used to describe the algorithm (e.g., "default=yes", "fips=yes"). Optional
implementation_function Pointer to the implementation function for the algorithm (e.g., cipher, digest, RNG interface). Mandatory
extra_data Reserved for future use; typically set to NULL. Optional

OSSL_DISPATCH to init provider

Provider 的初始化需要實現 OSSL_provider_init 函數,這是每個 Provider 的入口點。

c 复制代码
// Provider 的上下文結構
typedef struct {
    const OSSL_CORE_HANDLE *handle;  // 核心句柄
} CUSTOM_PROVIDER_CTX;

// 初始化函數
int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
                       const OSSL_DISPATCH *in,
                       const OSSL_DISPATCH **out,
                       void **provctx) {
    // 分配上下文
    CUSTOM_PROVIDER_CTX *ctx = OPENSSL_malloc(sizeof(CUSTOM_PROVIDER_CTX));
    if (ctx == NULL) {
        return 0;  // 初始化失敗
    }
    ctx->handle = handle;
    *provctx = ctx;

    // 將功能表導出給 OpenSSL
    *out = custom_provider_functions;
    return 1;  // 初始化成功
}

參數說明:

  1. handle (IN):由 OpenSSL 核心傳遞的句柄,用於與核心功能交互。
  2. in (IN):指向核心功能的 OSSL_DISPATCH 陣列,供 Provider 調用。
  3. out (OUT):指向 Provider 提供的 OSSL_DISPATCH 陣列,OpenSSL 通過它訪問 Provider 的功能。
  4. provctx (OUT):Provider 的上下文,用於維護 Provider 的狀態信息。
    每個 Provider 都需要提供一個 OSSL_DISPATCH 陣列,描述其支持的功能。
c 复制代码
static const OSSL_DISPATCH custom_provider_functions[] = {
    { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))custom_gettable_params },
    { OSSL_FUNC_PROVIDER_GET_PARAMS,      (void (*)(void))custom_get_params },
    { OSSL_FUNC_PROVIDER_TEARDOWN,       (void (*)(void))custom_teardown },
    { 0, NULL }
};

以下是按照要求生成的表格,包含功能ID、说明、必填/选填状态及对应的libopenssl API:

功能 ID 说明 必填或选填 libopenssl 会使用到的API
OSSL_FUNC_PROVIDER_GETTABLE_PARAMS 返回该 Provider 支持的参数名称列表,通常用于查询 Provider 的静态属性,例如名称或版本信息。 选填(Optional) OSSL_PROVIDER_gettable_params()
OSSL_FUNC_PROVIDER_GET_PARAMS 提供具体的参数值,例如返回 Provider 的名称或版本信息,与 GETTABLE_PARAMS 配合使用。 选填(Optional) OSSL_PROVIDER_get_params()
OSSL_FUNC_PROVIDER_QUERY_OPERATION 用于查询 Provider 支持的特定算法实现。这是 OpenSSL 与 Provider 交互的核心功能。 必填(Required) OSSL_PROVIDER_query_operation()
OSSL_FUNC_PROVIDER_TEARDOWN 用于释放 Provider 的资源,当 Provider 被卸载时调用。 选填(Optional) OSSL_PROVIDER_unload()
- - - OSSL_PROVIDER_free()

RNG 類型介紹

libssl 提供的RNG 根據使用場景不同,需要分別註冊不同的 OSSL_ALGORITHM。

硬體 TRNG 在實際應用中,主要作為種子源,提供高熵輸入以提升整體隨機數的不可預測性與安全性。

  • 完全適合使用硬體 TRNG:CTR-DRBG、HASH-DRBG、HMAC-DRBG、SEED-SRC、通用 RNG。
  • 不適合使用硬體 TRNG:TEST-RAND(僅用於測試環境)。

RNG algorithm life cycle

libssl 使用EVP_RAND_來呼叫RNG provider 提供的OSSL_DISPATCH ID 對應的函數指針

随机数生成器(RNG)生命周期阶段

阶段(Stage) 说明 OSSL_DISPATCH ID 分类
start 表示 RNG 尚未被分配,是任何生命周期过渡的起始状态。 - -
newed 表示 RNG 已经分配完成,但尚无法生成任何输出。 OSSL_FUNC_RAND_NEWCTX Context Management Functions
instantiated 表示 RNG 已完成设置,能够生成输出。 OSSL_FUNC_RAND_INSTANTIATE Random Number Generator Functions: NIST
uninstantiated 表示 RNG 已被关闭,无法再生成输出。 OSSL_FUNC_RAND_UNINSTANTIATE Random Number Generator Functions: NIST
freed 表示 RNG 已被释放,为所有生命周期过渡的终止状态。 OSSL_FUNC_RAND_FREECTX Context Management Functions

除了生命週期的函數指針以外,RNG 完成設置以後可呼叫EVP_RAND_,會呼叫到OSSL_DISPATCH OSSL_FUNC_RAND_,

但不同的RNG依照其特性,不一定包含所有ID

(舉例 EVP_RAND_get_params 呼叫到函數指針OSSL_FUNC_RAND_GET_PARAMS)

以下是基于提供内容生成的表格:

ID 說明
OSSL_FUNC_RAND_NONCE 生成指定強度的隨機數字元(nonce),長度介於 min_noncelen 到 max_noncelen 之間。若輸出緩衝區 out 為 NULL,則返回隨機數字元長度。
OSSL_FUNC_RAND_GET_SEED 確定性生成器從父生成器獲取種子材料。生成的種子字節數符合安全熵位數,總字節數介於 min_len 到 max_len 之間。種子材料指針存儲於 *buffer,需後續用 OSSL_FUNC_RAND_CLEAR_SEED 釋放。
OSSL_FUNC_RAND_CLEAR_SEED 釋放由 OSSL_FUNC_RAND_GET_SEED 分配的長度為 b_len 的種子緩衝區。
OSSL_FUNC_RAND_VERIFY_ZEROIZATION 檢查 RNG 內部狀態是否已歸零。此功能為 NIST 自測的一部分,其他情況下用途較少。
OSSL_FUNC_RAND_ENABLE_LOCKING 為 RNG 及其所有父 RNG 啟用鎖定。此後 RNG 可線程安全使用。
OSSL_FUNC_RAND_LOCK 鎖定 RNG,確保獨占訪問。
OSSL_FUNC_RAND_UNLOCK 解鎖 RNG。

Dynamic loadable module and build-in module

Provider 如何使用 build-in module

  1. 完成下列實作 OSSL_provider_init , OSSL_DISPATCH 與宣告OSSL_ALGORITHM的.c
  2. 修改 crypto/provider 路徑,加入第一步的.c
  3. 撰寫build.info
c 复制代码
SOURCE[../libcustom.a]=custom.c 

# It is necessary to have an explicit entry point
SOURCE[../custom]=custom_entry.c
  1. openssl (或其他使用此module的application) 使用custom build-in module,需要使用下列API
    以下是整理后的 OpenSSL Provider API 功能表格:
API 名稱 用途 參數說明
OSSL_PROVIDER_add_builtin() 註冊內建或自訂 Provider ctx: OpenSSL 上下文(NULL 表示全局) name: 模組名稱 init_fn: 初始化函數指標
OSSL_PROVIDER_try_load() 嘗試加載已註冊模組(未加載時才執行) ctx: OpenSSL 上下文 name: 模組名稱 retain_fallbacks: 是否保留回退選項(1=是)
OSSL_PROVIDER_load() 強制加載指定模組(無論當前狀態) ctx: OpenSSL 上下文 name: 模組名稱
OSSL_PROVIDER_unload() 卸載模組並釋放資源 prov: 已加載模組的對象指針
OSSL_PROVIDER_available() 檢查模組是否已加載且可用 ctx: OpenSSL 上下文 name: 模組名稱

結合上文Provider 初始化的段落舉例如何指定初始化OSSL_DISPATCH並加入OSSL_ALGORITHM

c 复制代码
static const OSSL_DISPATCH custom_rand_functions[] = {
    { OSSL_FUNC_RAND_NEWCTX, (void (*)(void))custom_rand_newctx },
    { OSSL_FUNC_RAND_FREECTX, (void (*)(void))custom_rand_freectx },
    { OSSL_FUNC_RAND_INSTANTIATE, (void (*)(void))custom_rand_instantiate },
    { OSSL_FUNC_RAND_UNINSTANTIATE, (void (*)(void))custom_rand_uninstantiate },
    { OSSL_FUNC_RAND_GENERATE, (void (*)(void))custom_rand_generate },
    { OSSL_FUNC_RAND_ENABLE_LOCKING, (void (*)(void))custom_rand_enable_locking },
    { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, (void (*)(void))custom_rand_gettable_ctx_params },
    { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void (*)(void))custom_rand_get_ctx_params },
    { 0, NULL }
};

static const OSSL_ALGORITHM custom_rand_algs[] = { { "CUSTOM", "provider=custom", custom_rand_functions },
                                               { NULL, NULL, NULL } };

static const OSSL_ALGORITHM *custom_query(void *provctx, int operation_id, int *no_cache)
{
    *no_cache = 0;
    switch (operation_id) {
    case OSSL_OP_RAND:
        return custom_rand_algs;
    default:
        return NULL;
    }
}
static const OSSL_DISPATCH custom_dispatch[] = { { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))OSSL_LIB_CTX_free },
                                             { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))custom_query },
                                             { 0, NULL } };

static int custom_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, const OSSL_DISPATCH **out,
                                void **provctx)
{
    OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
    if (libctx == NULL) {
        return 0;
    }
    *provctx = libctx;
    *out = custom_dispatch;
    return 1;
}

Provider 如何使用 dynamic loadable module

  1. 將帶有 OSSL_provider_init 以及 OSSL_DISPATCH 與宣告OSSL_ALGORITHM的.c 檔案,編譯為.so
  2. 配置 OpenSSL 確保可以找到動態模組的位置
bash 复制代码
export OPENSSL_MODULES=/path/to/modules

Dynamic loadable module 配置文件 openssl.cnf

c 复制代码
# 全局 OpenSSL 初始化配置
openssl_conf = openssl_init

# 初始化設置
[openssl_init]
providers = provider_section

# 提供者加載區域
[provider_section]
custom = custom_provider   # 加載自訂提供者
default = default_provider # 同時保留默認提供者,除非我已經實現了所有必需的加密、密鑰衍生、MAC 計算、簽署與驗證演算法,否則不能刪除

# 自訂提供者配置
[custom_provider]
module = $ENV::OPENSSL_MODULES/custom_provider.so
activate = 1  # 1 表示啟用 custom_provider
  1. 動態模組的初始化邏輯由 OSSL_provider_init 驅動

Openssl RNG 入口函數

實現provider 並正確加載以後,使用libssl 的application 調用 RAND_bytes 產生RNG,詳細 API 呼叫流程:

  1. 應用層調用 RAND_bytes,用於生成隨機數。RAND_bytes 定義在 crypto/rand/rand_lib.c,它是一個封裝函數,用於調用 RAND 方法
c 复制代码
int RAND_bytes(unsigned char *buf, int num) {
    return RAND_bytes_ex(NULL, buf, num, 0);
}
  1. RAND_bytes_ex 會從當前的 OpenSSL 全局上下文中獲取活躍的 Provider,並調用相應的 RAND 接口。
    通過 RAND_bytes_ex 調用隨機數生成 RAND_bytes_ex 會檢查 RAND 方法是否正確加載,然後調用 EVP_RAND_generate。
c 复制代码
int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num, unsigned int strength) {
    // 查詢 RAND Provider
    EVP_RAND_CTX *rand = EVP_RAND_fetch(ctx, "default", NULL);
    if (!rand)
        return 0;

    // 使用 RAND Provider 的生成方法
    return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
}
  1. EVP_RAND_generate 會通過 RAND Provider 的 OSSL_DISPATCH 表,調用 OSSL_FUNC_RAND_GENERATE。
c 复制代码
int EVP_RAND_generate(EVP_RAND_CTX *ctx, unsigned char *out, size_t outlen,
                      unsigned int strength, int prediction_resistance,
                      const unsigned char *addin, size_t addinlen) {
    const EVP_RAND *rand = ctx->rand;
    if (rand->generate)
        return rand->generate(ctx->provctx, out, outlen, strength,
                              prediction_resistance, addin, addinlen);
    return 0;
}

FAQ

  1. 已經使用openssl.cnf 指定某個custom provider(dynamic loadable module), application openssl 還需要使用OSSL_PROVIDER_load(顯式調用) 嗎?
    不需要顯式調用的情況:
    默認情況下自動加載所有配置的 Provider,當 OpenSSL 啟動時,會自動解析 openssl.cnf 中定義的所有 Provider,無需手動干預。如果在配置文件中通過 module 和 activate 配置了目標 Provider,OpenSSL 在啟動時會自動加載,應用程序可以直接使用。
  2. 需要顯式調用的情況
    1. 需要動態調整 Provider 行為:
      如果應用程序需要在運行期間動態切換或加載新的 Provider,而不是依賴啟動時的自動加載,就需要調用 OSSL_PROVIDER_load()。
    2. 需要在多上下文中使用不同的 Provider:
      如果應用程序使用了多個 OpenSSL 上下文(OSSL_LIB_CTX),且不同上下文需要不同的 Provider 配置,應用程序需要使用 OSSL_PROVIDER_load() 為每個上下文顯式加載 Provider。
    3. 需要加載未在配置文件中定義的 Provider:
      如果某個 Provider 未在 openssl.cnf 中配置,但需要在運行期間使用,應用程序可以通過顯式調用 OSSL_PROVIDER_load() 來加載它。

Reference

https://docs.openssl.org/master/man7/provider-rand/

https://docs.openssl.org/master/man7/life_cycle-rand/

https://docs.openssl.org/master/man3/OSSL_PROVIDER

https://docs.openssl.org/master/man3/EVP_RAND

相关推荐
看好多桂花树2 小时前
Nginx SSL/TLS 配置
网络·nginx·ssl
踏过山河,踏过海5 小时前
在SSL证书是有效的前提下,依旧显示“资源不安全
网络协议·安全·ssl
2501_9159214313 小时前
Charles 抓包 HTTPS 原理详解,从 CONNECT 到 SSL Proxying、常见问题与真机调试实战(含 Sniffmaster 补充方案)
android·网络协议·小程序·https·uni-app·iphone·ssl
2501_915918412 天前
HTTPS 请求抓包实战,从请求捕获到解密分析的逐步流程与工具组合(https 请求抓包、iOS 真机、SSL Pinning 排查)
android·ios·小程序·https·uni-app·iphone·ssl
2024暴富2 天前
Http升级Https使用Certbot申请证书并免费续期
http·阿里云·https·ssl
不努力谁会可怜你?2 天前
HTTPS报文在SSL/TLS证书安全隧道传输的原理
网络协议·https·stl·ssl·tls
游戏开发爱好者82 天前
iPhone HTTPS 抓包实战,原理、常见工具、SSL Pinning 问题与替代工具的解决方案
android·ios·小程序·https·uni-app·iphone·ssl
娅娅梨12 天前
HarmonyOS-ArkUI Web控件基础铺垫7-HTTP SSL认证图解 及 Charles抓包原理 及您为什么配置对了也抓不到数据
http·华为·ssl·harmonyos
影子240113 天前
java jdbc连接sqlserver2008R2版本数据库报错,驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接
java·数据库·ssl