openssl3.2 - note - Writing OpenSSL Provider Skeleton

news2024/11/24 20:59:07

文章目录

    • openssl3.2 - note - Writing OpenSSL Provider Skeleton
    • 概述
    • 笔记
    • 测试工程的建立
    • 复现的provider工程
    • 总结
    • Provider包含的头文件
    • openssl/core.h中的数据结构
    • 实现 OSSL_provider_init()
    • 看一下openssl自带的提供者
    • provider的openssl命令行测试
    • provider的本质是hook了openssl分发函数
    • 在自己的应用中直接写提供者, 不使用DLL
    • 响应参数的请求
    • 响应查询函数
    • 取openssl配置文件的条目内容
    • hook - provider卸载函数
    • 在provider初始化时, 分配自定义的provider_ctx空间
    • hook 错误处理
    • 测试
    • 是否provider被加载执行了?
    • 参数请求是否被provider响应了?
    • 查询是否被响应了?
    • 是否得到了配置文件中的数据?
    • 是否能设置错误到openssl API的调用者?
    • 备注
    • 备注
    • END

openssl3.2 - note - Writing OpenSSL Provider Skeleton

概述

到了openssl3.x, engine已经加了弃用的标记.
看了官方文档 Writing OpenSSL Provider Skeleton, 做个笔记
这个官方文档有用, 看懂后, 可以将自己的算法(或者硬件加密设备)接入openssl.
这个官方文档由openssl Provider架构的作者亲自讲解, NB.
学到东西了.

笔记

测试工程的建立

VS2019 + 动态链接库工程
在这里插入图片描述
设置好openssl的头文件路径. 库路径, 链接openssl的库函数(在初始化和卸载时, 不可避免的会调用库上下文的openssl API).
在Provider中, 尽量不要调用openssl的API, 对openssl无依赖(或少依赖), 官方推荐.

Provider既可以用单独的动态链接库实现, 也可以在自己的openssl应用的EXE中实现.
官方给的例子, 触发条件都是用openssl命令行, 所以实验最好使用DLL来做.
否则在EXE中写好, 不知道在EXE中怎么触发, 挺尴尬的, 没办法测试.
等自己弄明白了, 知道怎么触发Provider后, 就可以将Provider在自己的EXE中实现.
如果非要写EXE实现的provider, 那么还要自己去跟openssl命令行, 然后在自己的EXE中实现provider的触发.

最好是用DLL来实现Provider, 这样可以被其他工程复用. 测试也方便(用openssl.exe直接来测试).

复现的provider工程

官方例子中, 给的都是单个知识点. 我合在一起了.

//! \file dll_my_ossl_provider_for_test
//! \note openssl3.2 - note - Writing OpenSSL Provider Skeleton
//! 
//! 用opennssl命令触发Provider时, 可以带全路径, 但是比较繁琐. 还是设置环境变量后测试, 感觉好一些
//! 
//! 先打开带环境的openssl命令行, openssl命令本身运行正常
//! set OPENSSL_MODULES=D:\my_dev\my_local_git_prj\study\openSSL\exp\exp028C_my_ossl_provider\x64\Debug
//! set OPENSSL_MODULES
//! 然后再执行openssl命令来触发Provider
//! e.g. openssl list -provider dll_my_ossl_provider_for_test
//! 

#include "pch.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

// 官方建议 - 只包含最小化的openssl头文件, 且不链接openssl的库
#include <openssl/core.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>

// 如果不得已, 才链接openssl的库
// 看了官方的例子, 在OSSL_provider_init()/teardown()时, 都要操作自己创建的库上下文, 一点不用openssl的API挺难办到的
#pragma comment(lib, "libcrypto.lib")
#include <openssl/crypto.h> // for OSSL_LIB_CTX_free()

#define COMMITID "2024_0316"
#define E_UNSUPPORTED 0x2024 // 对应在provider中发生错误时, 用upcall设置的错误原因号码

// 自己需要啥样子的提供者上下文, 自己定义
// 如果没有在OSSL_provider_init()中分配提供者上下文, 进入OSSL_FUNC_PROVIDER_TEARDOWN处理时, 上下文指针就为NULL
struct provctx_st {
	int dummy;

	OSSL_LIB_CTX* libctx;

	const OSSL_CORE_HANDLE* handle;
	OSSL_FUNC_core_new_error_fn* up_new_error;
	OSSL_FUNC_core_set_error_debug_fn* up_set_error_debug;
	OSSL_FUNC_core_vset_error_fn* up_vset_error;
};

static const OSSL_PARAM* gettable_params(void* provctx);
static int get_params(void* provctx, OSSL_PARAM* params);
static const OSSL_ALGORITHM* query(void* provctx, int operation_id, int* no_cache);
void teardown(void* provctx);
const OSSL_ITEM* get_reasons(void* provctx);

// provider做的事, 实际上是hook openssl的分发函数, 在provider中实现openssl上层需要的分发函数
const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))gettable_params },
	{ OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))get_params },
	{ OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))query },
	{ OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (void (*)(void))get_reasons }, // 错误处理, 当发生错误时, openssl回来要对应错误号码的错误原因
	{ OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))teardown },
	
	{ 0, NULL }
};

// 以下2个宏必须定义一个
#define USE_DISPATCH_LIB_CTX // 创建在provider中使用的库上下文 - 自包含型
// #define USE_CHILD_LIB_CTX // 创建在provider中使用的库上下文 - 继承型库

#if ((defined USE_DISPATCH_LIB_CTX) && (defined USE_CHILD_LIB_CTX))
#error only define ONE macro from USE_DISPATCH_LIB_CTX, USE_CHILD_LIB_CTX
#endif

OSSL_provider_init_fn OSSL_provider_init; /* Check function signature */
int OSSL_provider_init(const OSSL_CORE_HANDLE* handle, const OSSL_DISPATCH* in_dispatch, const OSSL_DISPATCH** out, void** provctx)
{
	const OSSL_DISPATCH* _in = NULL;

	fprintf(stderr, ">> dll_my_ossl_provider_for_test::OSSL_provider_init()\n");
	*out = g_provider_dispatch_ary;

	// 如果自己分配了提供者上下文空间, 在提供者卸载时, 就会清理提供者上下文
	struct provctx_st* pctx = (struct provctx_st*)malloc(sizeof(struct provctx_st));
	memset(pctx, 0, sizeof(struct provctx_st));
	*provctx = pctx; // 初始化时, 给提供者上下文分配空间

	// provider自己创建一个库的上下文自己用(将新的库上下文保存起来, 等teardown时释放)
	// provider自己创建的库上下文分为2种(自包含型和继承型)
	/* A library context that's independent of the calling application */

#if defined USE_DISPATCH_LIB_CTX
	printf("OSSL_LIB_CTX_new_from_dispatch\r\n");
	pctx->libctx = OSSL_LIB_CTX_new_from_dispatch(handle, in_dispatch); // 自包含的库上下文, 自己加载其他以来的provider, 除了upcall, 不依赖provider的调用者
#elif defined USE_CHILD_LIB_CTX
	printf("OSSL_LIB_CTX_new_child\r\n");
	pctx->libctx = OSSL_LIB_CTX_new_child(handle, in_dispatch); // 从调用者继承的库上下文, 可以访问调用者加载的所有provider
#else
	assert(false);
#endif

	if (NULL == pctx->libctx) {
		teardown(*provctx);
		*provctx = NULL;
		return 0;
	}

	OSSL_FUNC_core_new_error_fn* c_new_error = NULL;
	OSSL_FUNC_core_set_error_debug_fn* c_set_error_debug = NULL;
	OSSL_FUNC_core_vset_error_fn* c_vset_error = NULL;

	// 取upcall指针
	_in = in_dispatch; // ! 如果有改变指针的行为, 需要操作原始指针的副本
	for (; _in != NULL && _in->function_id; _in++) {
		switch (_in->function_id) {
		case OSSL_FUNC_CORE_NEW_ERROR:
			// printf("find OSSL_FUNC_CORE_NEW_ERROR\r\n");
			c_new_error = OSSL_FUNC_core_new_error(_in);
			break;
		case OSSL_FUNC_CORE_SET_ERROR_DEBUG:
			// printf("find OSSL_FUNC_CORE_SET_ERROR_DEBUG\r\n");
			c_set_error_debug = OSSL_FUNC_core_set_error_debug(_in);
			break;
		case OSSL_FUNC_CORE_VSET_ERROR:
			// printf("find OSSL_FUNC_CORE_VSET_ERROR\r\n");
			c_vset_error = OSSL_FUNC_core_vset_error(_in);
			break;
		default:
			break;
		}
	}

	pctx->handle = handle;
	pctx->up_new_error = c_new_error;
	pctx->up_set_error_debug = c_set_error_debug;
	pctx->up_vset_error = c_vset_error;

	OSSL_FUNC_core_gettable_params_fn* c_gettable_params = NULL;
	OSSL_FUNC_core_get_params_fn* c_get_params = NULL;

	// 取upcall指针
	_in = in_dispatch; // ! 如果有改变指针的行为, 需要操作原始指针的副本
	for (; _in != NULL && _in->function_id; _in++) {
		switch (_in->function_id) {
		case OSSL_FUNC_CORE_GETTABLE_PARAMS:
			c_gettable_params = OSSL_FUNC_core_gettable_params(_in);
			break;

		case OSSL_FUNC_CORE_GET_PARAMS:
			c_get_params = OSSL_FUNC_core_get_params(_in);
			break;
		}
	}

	// 从自己的提供者内部遍历取openssl配置文件的条目名称定义信息
	assert(NULL != c_gettable_params);
	const OSSL_PARAM* p = (OSSL_PARAM*)c_gettable_params(handle);

	printf("print available libcrypto param\r\n");
	for (; ((p != NULL) && (p->key != NULL)); p++)
	{
		printf("available libcrypto param: %s [type %u, size %zu]\n", p->key, p->data_type, p->data_size);
	}

	// 取特定的已知参数值(如果取不存在的参数值, 取不到)
	printf("get special param:\r\n");
	const char* config_openssl_version = NULL;
	const char* config_something = NULL;
	OSSL_PARAM config_params[] = {
		{ OSSL_PROV_PARAM_CORE_VERSION, OSSL_PARAM_UTF8_PTR,
		  &config_openssl_version, 0, (size_t)-1 },
		{ "something", OSSL_PARAM_UTF8_PTR,
		  &config_something, 0, (size_t)-1 },
		{ NULL, 0, NULL, 0, 0 }
	};

	if (!c_get_params(handle, config_params))
		return 0;

	if (config_params[0].return_size != (size_t)-1)
	{
		printf("libcrypto param '%s': %s\n", OSSL_PROV_PARAM_CORE_VERSION, config_openssl_version);
	}

	if (config_params[1].return_size != (size_t)-1)
	{
		printf("libcrypto param 'something': %s\n", config_something);
	}

	return 1;
}


static void provider_err(struct provctx_st* pctx, int reason, const char* file, unsigned int line, const char* func, const char* fmt, ...)
{
	va_list ap;

	// 在错误处理函数中, 用upcall向openssl API的调用者设置了错误
	va_start(ap, fmt);
	pctx->up_new_error(pctx->handle);
	pctx->up_set_error_debug(pctx->handle, file, line, func);
	pctx->up_vset_error(pctx->handle, reason, fmt, ap);
	va_end(ap);
}

// __VA_OPT__ 在windows下没有, 如果有变参, 只能直接写最终的函数, 无法使用宏
//#define ERR(pctx,reason,fmt,...) \
//    err((pctx), (reason), __FILE__, __LINE__, __func__, fmt     \
//        __VA_OPT__(,) __VA_ARGS__)

static const OSSL_PARAM* gettable_params(void* provctx)
{
	// gettable_params 返回Provider有哪些参数可以拿
	printf(">> gettable_params\r\n");
	static const OSSL_PARAM params[] = {
		{ OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },

		{ NULL, 0, NULL, 0, 0 }
	};
	return params;
}

static int get_params(void* provctx, OSSL_PARAM* params)
{
	// get_params 去响应具体的参数(gettable_params()中指定)
	const char** r = NULL;

	printf(">> get_params\r\n");
	for (; params != NULL && params->key != NULL; params++) {
		if (strcmp(params->key, OSSL_PROV_PARAM_BUILDINFO) == 0) {
			if (params->data_type == OSSL_PARAM_UTF8_PTR) {
				r = (const char**)params->data;
				*r = "commit " COMMITID;
				params->return_size = strlen(*r);
			}
			else {
				return 0;
			}
		}
	}
	return 1;
}

static OSSL_FUNC_digest_newctx_fn blargh_h_newctx;
static void* blargh_h_newctx(void* provctx)
{
	return NULL;
}

static OSSL_FUNC_digest_freectx_fn blargh_h_freectx;
static void blargh_h_freectx(void* bctx)
{
}

static OSSL_FUNC_digest_init_fn blargh_h_init;
static int blargh_h_init(void* bctx, const OSSL_PARAM* params)
{
	return 0;
}

static OSSL_FUNC_digest_update_fn blargh_h_update;
static int blargh_h_update(void* bctx, const unsigned char* m, size_t s)
{
	return 0;
}

static OSSL_FUNC_digest_final_fn blargh_h_final;
static int blargh_h_final(void* bctx, unsigned char* d, size_t* l, size_t s)
{
	return 0;
}

static OSSL_FUNC_digest_get_params_fn blargh_h_get_params;
static int blargh_h_get_params(OSSL_PARAM* params)
{
	for (; params != NULL && params->key != NULL; params++) {
		if (strcmp(params->key, OSSL_DIGEST_PARAM_BLOCK_SIZE) == 0) {
			if (params->data_type == OSSL_PARAM_UNSIGNED_INTEGER
				&& params->data_size == sizeof(size_t)) {
				size_t* r = (size_t*)params->data;
				*r = 1;
			}
			else {
				return 0;
			}
		}
		else if (strcmp(params->key, OSSL_DIGEST_PARAM_SIZE) == 0) {
			if (params->data_type == OSSL_PARAM_UNSIGNED_INTEGER
				&& params->data_size == sizeof(size_t)) {
				size_t* r = (size_t*)params->data;
				*r = 1;
			}
			else {
				return 0;
			}
		}
	}
	return 1;
}

// 如果要实现一个hash算法, 则该hash操作的每一步的算法都要hook
static const OSSL_DISPATCH blargh_hash_impl[] = {
	{ OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))blargh_h_newctx },
	{ OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blargh_h_freectx },
	{ OSSL_FUNC_DIGEST_INIT, (void (*)(void))blargh_h_init },
	{ OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))blargh_h_update },
	{ OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blargh_h_final },
	{ OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blargh_h_get_params },
	{ 0, NULL }
};

static const OSSL_ALGORITHM hashes[] = {
	{ "BLARG:id-blargh:1.2.3.4.5", "x.author=bleah", blargh_hash_impl, "BLARG is a fictious generic algorithm" },
	{ NULL, NULL, NULL, NULL }
};

static OSSL_FUNC_provider_query_operation_fn query;
static const OSSL_ALGORITHM* query(void* provctx, int operation_id, int* no_cache)
{
	printf(">> query\r\n");

	*no_cache = 0;

	switch (operation_id) {
	case OSSL_OP_DIGEST:
		return hashes;
		break;
	default:
		break;
	}

	provider_err((struct provctx_st*)provctx, E_UNSUPPORTED, __FILE__, __LINE__, __func__, "operation_id=%d", operation_id);
	return NULL;
}

static OSSL_FUNC_provider_teardown_fn teardown;
void teardown(void* provctx)
{
	struct provctx_st* pctx = NULL; // provctx;

	printf("provider teardown, provctx = %s\n", (NULL == provctx) ? "NULL" : "not NULL");
	if (NULL != provctx)
	{
		// 自己在OSSL_provider_init()中分配了上下文
		pctx = (provctx_st*)provctx;

		if (NULL != pctx->libctx)
		{
			printf("OSSL_LIB_CTX_free(pctx->libctx)\r\n");
			OSSL_LIB_CTX_free(pctx->libctx);
		}

		printf("free(provctx)\n");
		free(provctx);  // OSSL_LIB_CTX_free(pctx->libctx); // 尽量不要在提供者中调用openssl API
	}
}

const OSSL_ITEM* get_reasons(void* provctx)
{
	// 向upcall设置的错误是啥(e.g. return 1), 就到get_reasons()中返回的表中找(reasons[]->id == 1)的reasons[]->ptr作为错误原因
	static const OSSL_ITEM reasons[] = {
	  { E_UNSUPPORTED, (void*)"Hello, world!  This is unsupported" },
	  { 0, NULL }
	};
	return reasons;
}

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


总结

这个官方demo, 如果用文字将细节描述出来, 挺难的. 毕竟不是一个知识点.
主要体会都写在复现工程中了, 这里总结一些零碎.

Provider包含的头文件

Provider最好只包含以下2个头文件, 作为对openssl的最小包含, 让Provider对openssl的依赖最小化.

#include <openssl/core.h>
#include <openssl/core_dispatch.h>

Provider可以用任何语言来写(为了兼容opensslAPI的调用约定, 最好使用C/C++来写), 在提供者内部, 不要使用openssl的API.
在自己的Provider中要实现能提供的算法(加解密/hash/MAC/sign/veirfy_sign…)
官方的这个例子, 只是一个provider框架, 离干活的provider还很远.
不过, 过一遍这个例子, 再看成品provider工程, 原理就清楚了.

openssl/core.h中的数据结构

基本的数据结构都在 openssl/types.h 中定义

typedef struct ossl_dispatch_st OSSL_DISPATCH;
typedef struct ossl_algorithm_st OSSL_ALGORITHM;
typedef struct ossl_item_st OSSL_ITEM;
typedef struct ossl_param_st OSSL_PARAM;

这些数据结构见名知意.

// 带名字的函数指针
struct ossl_dispatch_st {
    int function_id;
    void (*function)(void);
};

// 带名字的数据指针
struct ossl_item_st {
    unsigned int id;
    void *ptr;
};

// 带名字的参数结构
struct ossl_param_st {
    const char *key;             /* the name of the parameter */
    unsigned int data_type;      /* declare what kind of content is in buffer */
    void *data;                  /* value being passed in or out */
    size_t data_size;            /* data size */
    size_t return_size;          /* returned content size */
};

// 算法实现结构(有名字, 有属性, 有实现算法用的函数指针)
struct ossl_algorithm_st {
    const char *algorithm_names;     /* key */
    const char *property_definition; /* key */
    const OSSL_DISPATCH *implementation;
    const char *algorithm_description;
};

实现 OSSL_provider_init()

实现OSSL_provider_init()后的DLL, 就是一个最小化的Provider

看一下openssl自带的提供者

openssl list -providers
Providers:
  default
    name: OpenSSL Default Provider
    version: 3.2.0
    status: active

即使设置了OPENSSL_MODULES环境变量, 自己的provider已经写好了(也在OPENSSL_MODULES环境变量), 也看不到自己的provider.
但是用openssl指定了自己的provider是可以操作的.

provider的openssl命令行测试

尝试执行 openssl list -provider 指定全路径, 这时, 可以看到有反应, DLL初始化接口被执行了.

D:\my_tmp>openssl list -provider D:\my_dev\my_local_git_prj\study\openSSL\exp\exp028C_my_ossl_provider\x64\Debug\dll_my_ossl_provider_for_test.dll
>> OSSL_provider_init

因为openssl编译时, 有个默认的模块载入路径(也有可能在配置文件中), 现在必须自己指定全路径才行.

也可以指定模块的环境变量也行

set OPENSSL_MODULES=D:\my_dev\my_local_git_prj\study\openSSL\exp\exp028C_my_ossl_provider\x64\Debug\
set OPENSSL_MODULES

// dll的后缀不能加, openssl会自己当作是.dll, 否则报错
openssl list -provider dll_my_ossl_provider_for_test

provider的本质是hook了openssl分发函数

// provider做的事, 实际上是hook openssl的分发函数, 在provider中实现openssl上层需要的分发函数
const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))gettable_params },
	{ OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))get_params },
	{ OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))query },
	{ OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (void (*)(void))get_reasons }, // 错误处理, 当发生错误时, openssl回来要对应错误号码的错误原因
	{ OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))teardown },
	
	{ 0, NULL }
};

在自己的应用中直接写提供者, 不使用DLL

这种提供者做实验方便, 但是在不熟悉Provider时, 想测试触发Provider蛮烦/尴尬

/*!
* \file main.cpp
*/

#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "CMemHookRec.h"

#include "openssl/provider.h"

void my_openssl_app();

int main(int argc, char** argv)
{
	setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞
	mem_hook();

	my_openssl_app();

	mem_unhook();

	return 0;
}

// OSSL_provider_init_fn my_prov_init;

int OSSL_provider_init(const OSSL_CORE_HANDLE* handle,
	const OSSL_DISPATCH* in,
	const OSSL_DISPATCH** out,
	void** provctx);

void my_openssl_app()
{
	OSSL_PROVIDER* prov = NULL;
	if (OSSL_PROVIDER_add_builtin(NULL, "my_ossl_provider_on_THE_EXE", &OSSL_provider_init))
	{
		prov = OSSL_PROVIDER_load(NULL, "my_ossl_provider_on_THE_EXE");
		if (NULL != prov)
		{
			OSSL_PROVIDER_unload(prov);
		}
	}

}

static const OSSL_DISPATCH provfns[] =
{
	{0, NULL}
};


int OSSL_provider_init(const OSSL_CORE_HANDLE* handle,
	const OSSL_DISPATCH* in,
	const OSSL_DISPATCH** out,
	void** provctx)
{
	// 提供者初始化函数被执行到了
	printf("dll_my_ossl_provider - OSSL_provider_init()\r\n");

	*out = provfns;
	return 1;
}

响应参数的请求

const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))gettable_params }, // hook OSSL_FUNC_PROVIDER_GETTABLE_PARAMS
	{ OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))get_params }, // hook OSSL_FUNC_PROVIDER_GET_PARAMS

响应查询函数

进行算法操作时, openssl都会根据需要, 随时向Provider要东西(查询). 所以要实现openssl查询函数的响应.
实现查询响应函数

const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))query }, // hook OSSL_FUNC_PROVIDER_QUERY_OPERATION

取openssl配置文件的条目内容

都是在OSSL_provider_init()中曲参数
遍历取openssl配置文件的条目定义信息
主动取openssl配置文件的条目值

取信息时, 用的都是upcall(调用者传来的函数指针数组). 先从分发函数指针数组中得到upcall指针, 再用upcall指针取参数值.
_in = in_dispatch; // ! 如果有改变指针的行为, 需要操作原始指针的副本

hook - provider卸载函数

const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))teardown }, // hook OSSL_FUNC_PROVIDER_TEARDOWN

在provider初始化时, 分配自定义的provider_ctx空间

// provider自己创建一个库的上下文自己用(将新的库上下文保存起来, 等teardown时释放)
// provider自己创建的库上下文分为2种(自包含型和继承型)

hook 错误处理

const OSSL_DISPATCH g_provider_dispatch_ary[] = {
	{ OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (void (*)(void))get_reasons }, // OSSL_FUNC_PROVIDER_GET_REASON_STRINGS

get_reasons只是实现了根据错误号取错误信息的回调.

具体错误设置, 要自己设置(e.g. 某个回调操作中失败, 用upcall设置错误号码到openssl API调用者那里才行)

provider_err((struct provctx_st*)provctx, E_UNSUPPORTED, __FILE__, __LINE__, __func__, "operation_id=%d", operation_id);
static void provider_err(struct provctx_st* pctx, int reason, const char* file, unsigned int line, const char* func, const char* fmt, ...)
{
	va_list ap;

	// 在错误处理函数中, 用upcall向openssl API的调用者设置了错误
	va_start(ap, fmt);
	pctx->up_new_error(pctx->handle);
	pctx->up_set_error_debug(pctx->handle, file, line, func);
	pctx->up_vset_error(pctx->handle, reason, fmt, ap);
	va_end(ap);
}

测试

启动自己的openssl命令行的批处理, 包含了openssl bin目录的path
设置provider所在目录为环境变量OPENSSL_MODULES
OPENSSL_MODULES=D:\my_dev\my_local_git_prj\study\openSSL\exp\exp028C_my_ossl_provider\x64\Debug
OPENSSL_MODULES

然后就可以运行openssl命令行了

是否provider被加载执行了?

D:\my_tmp>openssl list -provider dll_my_ossl_provider_for_test -verbose
>> dll_my_ossl_provider_for_test::OSSL_provider_init() // 这句在OSSL_provider_init()中打印的, 看到此信息, 说明provider被加载了
OSSL_LIB_CTX_new_from_dispatch
print available libcrypto param
available libcrypto param: openssl-version [type 6, size 0]
available libcrypto param: provider-name [type 6, size 0]
available libcrypto param: module-filename [type 6, size 0]
get special param:
libcrypto param 'openssl-version': 3.2.0
provider teardown, provctx = not NULL
OSSL_LIB_CTX_free(pctx->libctx)
free(provctx)

参数请求是否被provider响应了?

D:\my_tmp>openssl list -provider dll_my_ossl_provider_for_test -verbose -providers
>> dll_my_ossl_provider_for_test::OSSL_provider_init()
OSSL_LIB_CTX_new_from_dispatch
print available libcrypto param
available libcrypto param: openssl-version [type 6, size 0]
available libcrypto param: provider-name [type 6, size 0]
available libcrypto param: module-filename [type 6, size 0]
get special param:
libcrypto param 'openssl-version': 3.2.0
Providers:
>> get_params // !
  dll_my_ossl_provider_for_test
    build info: commit 2024_0316
>> gettable_params // 进了gettable_params(), get_params(), 参数就响应了
    gettable provider parameters: 
      buildinfo: pointer to a UTF8 encoded string (arbitrary size)
provider teardown, provctx = not NULL
OSSL_LIB_CTX_free(pctx->libctx)
free(provctx)

查询是否被响应了?

D:\my_tmp>openssl list -provider dll_my_ossl_provider_for_test -verbose -digest-algorithms
>> dll_my_ossl_provider_for_test::OSSL_provider_init()
OSSL_LIB_CTX_new_from_dispatch
print available libcrypto param
available libcrypto param: openssl-version [type 6, size 0]
available libcrypto param: provider-name [type 6, size 0]
available libcrypto param: module-filename [type 6, size 0]
get special param:
libcrypto param 'openssl-version': 3.2.0
Legacy:
  RSA-MD4 => MD4
  RSA-MD5 => MD5
  // ...
  whirlpool
Provided:
>> query // 可以看到进了hook后的查询分发函数
>> query
  { 1.2.3.4.5, BLARG, id-blargh } @ dll_my_ossl_provider_for_test
    description: BLARG is a fictious generic algorithm
provider teardown, provctx = not NULL
OSSL_LIB_CTX_free(pctx->libctx)
free(provctx)

是否得到了配置文件中的数据?

D:\my_tmp>openssl list -provider dll_my_ossl_provider_for_test -verbose
>> dll_my_ossl_provider_for_test::OSSL_provider_init()
OSSL_LIB_CTX_new_from_dispatch
print available libcrypto param // !
available libcrypto param: openssl-version [type 6, size 0] // 遍历得到了调用者上下文中的配置项定义信息
available libcrypto param: provider-name [type 6, size 0]
available libcrypto param: module-filename [type 6, size 0]
get special param: // !
libcrypto param 'openssl-version': 3.2.0 // 直接去得到了配置文件项的值
provider teardown, provctx = not NULL
OSSL_LIB_CTX_free(pctx->libctx)
free(provctx)

是否能设置错误到openssl API的调用者?

D:\my_tmp>openssl enc -provider dll_my_ossl_provider_for_test -cipher_not_exist
>> dll_my_ossl_provider_for_test::OSSL_provider_init()
OSSL_LIB_CTX_new_from_dispatch
print available libcrypto param
available libcrypto param: openssl-version [type 6, size 0]
available libcrypto param: provider-name [type 6, size 0]
available libcrypto param: module-filename [type 6, size 0]
get special param:
libcrypto param 'openssl-version': 3.2.0
>> query
enc: Unknown option or cipher: cipher_not_exist
enc: Use -help for summary.
// ! 如果执行了不支持的算法(e.g. 这个官方例子只实现了一个伪hash, 没有实现加密算法), 这里能看到工程中设置错误的文件+行号
145B0500:error:40002024:dll_my_ossl_provider_for_test:query:Hello, world!  This is unsupported:D:\my_dev\my_local_git_prj\study\openSSL\exp\exp028C_my_ossl_provider\dll_my_ossl_provider_for_test.cpp:328:operation_id=2
145B0500:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:342:Global default library context, Algorithm (cipher_not_exist : 0), Properties (<null>)
provider teardown, provctx = not NULL
OSSL_LIB_CTX_free(pctx->libctx)
free(provctx)

备注

看完官方作者讲解后, 再参照openssl中已有的provider实现, 自己整一个新的能用的provider应该没问题.

备注

OpenSSL Provider 作者讲的这篇官方文档, 信息量真大.
官方跟着源码库走的demo工程, 完全没提到如何自己做Provider.

能自己做Provider后, 最主要的用途是用openssl接入硬件加密设备(e.g. 第三方的硬件加密机).
虽然也能加入自己的算法, 但密码学家还是少数.

不过可以自己做Ukey, 在Ukey中隐藏一些私有算法(别管算法丑不丑, 只会软件逆向的人拿不到:P), 这是可以的.
然后openssl通过providerDLL, providerDLL再通过USB通讯向Ukey发送要处理的数据, 然后由Ukey返回处理完的数据, 再返回openssl接口. 这看着挺顺的.

现在很多MCU的自身保护都很强, 一般人无法将程序的2进制实现读出来. 总之, 能破硬件固件的人比会软件逆向的人还是少很多.

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1522402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向&#xff1a;计算机视觉&#xff0c;自然语言&#xff0c;语音识别。 机器学习核心组件&#xff1a;1 数据集(data)&#xff0c;2 前向传播的model(net)&#xff0c;3 目标函数(loss)&#xff0c; 4 调整模型参数和优化函数的算法…

【研发管理】产品经理-基础认知

导读&#xff1a;产品经理&#xff08;Product Manager&#xff09;是一个负责产品的全周期管理的职位&#xff0c;他们不仅参与产品的设计、开发、推广和销售&#xff0c;还涉及到产品的市场调研、用户需求分析、竞争分析、产品规划、产品测试以及后续的产品迭代等多个环节。产…

安装snap再安装flutter再安装localsend@Ubuntu(FreeBSD下未成功)

Localsend介绍 localsend是一个跨平台的文件传送软件&#xff0c;可以在Windows、MacOS、Linux、Android和IOS下互相传送文件&#xff0c;只要在同一个局域网即可。 localsend官网&#xff1a;LocalSend 尝试安装localsend&#xff0c;发现需要使用flutter&#xff0c; 安装f…

【AI】Ubuntu系统深度学习框架的神经网络图绘制

一、Graphviz 在Ubuntu上安装Graphviz&#xff0c;可以使用命令行工具apt进行安装。 安装Graphviz的步骤相对简单。打开终端&#xff0c;输入以下命令更新软件包列表&#xff1a;sudo apt update。之后&#xff0c;使用命令sudo apt install graphviz来安装Graphviz软件包。为…

挑战杯 机器视觉人体跌倒检测系统 - opencv python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 机器视觉人体跌倒检测系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&…

前端之CSS 创建css--行内引入、内联样式、外联样式

创建css有三种创建样式&#xff0c;行内引入、内联引入、外联引入。 行内引入 在行内标签引入 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>行内样式</title> </head> <body>…

【安全类书籍-3】XSS跨站脚剖析与防御

目录 内容简介 作用 下载地址 内容简介 这本书涵盖以下几点: XSS攻击原理:解释XSS是如何利用Web应用未能有效过滤用户输入的缺陷,将恶意脚本注入到网页中,当其他用户访问时被执行,实现攻击者的目的,例如窃取用户会话凭证、实施钓鱼攻击等。 XSS分类:分为存储型XSS(…

单片机FLASH深度解析和编程实践(下)

本篇文章将同大家分享单片机FLASH编程的相关寄存器和寄存器操作及库函数操作。本篇文章依然以STM32单片机为例进行解析。有关FLASH的基本原理和实现方法&#xff0c;大家可以参考上一篇文章&#xff1a;单片机FLASH深度解析和编程实践&#xff08;上&#xff09;-CSDN博客 目录…

ChatGPT编程—实现小工具软件(文件查找和筛选)

ChatGPT编程—实现小工具软件(文件查找和筛选) 今天借助[小蜜蜂AI][https://zglg.work]网站的ChatGPT编程实现一个功能&#xff1a;根据特定需求结合通配符和其他条件来进行文件查找和筛选。在这个例子中&#xff0c;我们将创建一个函数find_files&#xff0c;它接受用户输入的…

solr/ES 分词插件Jcseg设置自定义词库

步骤&#xff1a; 1、找到配置文件jcseg-core/target/classes/jcseg.properties修改配置&#xff1a; 下载地址: https://gitee.com/lionsoul/jcseg#5-如何自定义使用词库 lexicon.path {jar.dir}/../custom-word 设置lexicon路径&#xff0c;我们这个配置可以自定义&#xf…

Java 与 Go:可变数组

可变数组&#xff08;也称为动态数组&#xff09;是一种可以在运行时动态增加或减少其大小的数据结构。由于其动态分配大小&#xff0c;灵活性增删改查&#xff0c;动态地管理内存&#xff08;在需要时动态分配内存空间&#xff0c;以适应数据结构的大小变化&#xff0c;而不会…

NCV1117ST50T3G线性稳压器芯片中文资料规格书PDF数据手册引脚图图片价格参数

产品概述&#xff1a; NCP1117系列为低压差&#xff08;LDO&#xff09;正向线性电压稳压器&#xff0c;能够提供超过1.0A的输出电流&#xff0c;800mA时温度范围内最大压差为1.2V。这一系列包括八个固定输出电压&#xff1a;1.5V、1.8V、2.0V、2.5V、2.85V、3.3V、5.0V 和 12…

​​SQLiteC/C++接口详细介绍之sqlite3类(十一)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;​​SQLiteC/C接口详细介绍之sqlite3类&#xff08;十&#xff09; 下一篇&#xff1a;​​SQLiteC/C接口详细介绍之sqlite3类&#xff08;十二&#xff09;&#xff08;未发表&#xff09; 33.sq…

【RS422】基于未来科技FT4232HL芯片的多波特率串口通信收发实现

功能简介 串行通信接口常常用于在计算机和低速外部设备之间传输数据。串口通信存在多种标准&#xff0c;以RS422为例&#xff0c;它将数据分成多个位&#xff0c;采用异步通信方式进行传输。   本文基于Xilinx VCU128 FPGA开发板&#xff0c;对RS422串口通信进行学习。   根…

openlayers 入门教程(二):map 篇

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

基于CNN多阶段图像超分+去噪(超级简单版)

这是之前的一项工作&#xff0c;非常简单&#xff0c;简单的复现了两个算法&#xff0c;然后把它们串起来了。 可执行的程序链接&#xff1a;CSDN; Github 我们分成两部分进行讲解&#xff1a; 1. 图像去噪 1.1 基本思路 图像的去噪工作基于很普通的CNN去噪&#xff0c;效…

Java基础 - 9 - 集合进阶(二)

一. Collection的其他相关知识 1.1 可变参数 可变参数就是一种特殊形参&#xff0c;定义在方法、构造器的形参列表里&#xff0c;格式是&#xff1a;数据类型…参数名称; 可变参数的特点和好处 特点&#xff1a;可以不传数据给它&#xff1b;可以传一个或者同时传多个数据给…

2核4g服务器够用吗?

2核4G服务器够用吗&#xff1f;够用。阿腾云以2核4G5M服务器搭建网站为例&#xff0c;5M带宽下载速度峰值可达640KB/秒&#xff0c;阿腾云以搭建网站为例&#xff0c;假设优化后平均大小为60KB&#xff0c;则5M带宽可支撑10个用户同时在1秒内打开网站&#xff0c;并发数为10&am…

Github Copilot 工具,无需账号,一键激活

① 无需账号&#xff0c;100%认证成功&#xff01;0风险&#xff0c;可联网可更新&#xff0c;&#xff0c;支持copilot版本升级&#xff0c;支持chat ② 支持windows、mac、linux系统等设备 ③一号通用&#xff0c;支持所有IDE(AppCode,CLion,DataGrip,GoLand,IntelliJ IDEA …

基于FPGA的光纤通信系统设计

文章目录 光纤通信系统的组成发送端FPGA端口定义状态机设计代码示例 接收端功能模块端口定义状态机设计 光纤通信系统的组成 发送端FPGA 发送控制逻辑、数据编码、校验码生成、缓存控制、时钟控制 端口定义 状态机设计 代码示例 接收端功能模块 接收端控制逻辑、数据解码、…