如何整合 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

相关推荐
2601_9670195110 小时前
服务器SSL配置要点,规避HTTPS访问异常风险
服务器·https·ssl
Lsetea2 天前
ACME 报 rate limit:Let‘s Encrypt 限速类型与重试策略
运维·https·证书·ssl·acme
今夕资源网3 天前
本地SSL证书生成工具本地 HTTPS 配置不用再敲命令:CertTool 一站式管理自签证书与 Hosts
网络协议·https·ssl·ssl证书生成·ssl本地测试证书
瓦格哈儿5 天前
删掉旧 SSL 证书会影响线上业务吗?风险点梳理
网络协议·https·ssl
姚不倒5 天前
HAProxy 系列(二):健康检查与 SSL 卸载 — 保障可用性与性能
运维·网络·网络协议·ssl·haproxy
MyFreeIT7 天前
Let‘s Encrypt SSL 製作免費證書
网络协议·https·ssl
00后程序员张7 天前
Android证书绑定抓包失败?Android SSL Pinning绕过实战指南
android·网络协议·计算机网络·网络安全·adb·https·ssl
武帝为此7 天前
【TLS SSL 介绍】
网络·网络协议·ssl
吠品10 天前
SQL Server 版本查询的几种实用方法
数据库·网络协议·ssl
瓦格哈儿11 天前
内网环境也需要证书?聊聊内网 IP 证书的适用边界
网络协议·ssl