当前位置: 首页 > web >正文

[SKE]UVM环境下OpenSSL加密算法参考模型设计

UVM环境下OpenSSL加密算法参考模型设计

       摘要:在UVM测试平台中,需要一个基于OpenSSL的Golden Model来验证硬件加密模块。OpenSSL提供了丰富的加密接口:对称加密可使用EVP接口(例如调用EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)初始化AES上下文,并使用EVP_EncryptUpdate/EVP_EncryptFinal_ex执行加密 (tongsuo.readthedocs.io)),国密SM4也可以用类似方式调用EVP_sm4_cbc()等函数 (docs.openssl.org);DES和3DES可用EVP_des_cbc()/EVP_des_ede3_cbc()等函数处理;RSA则提供了RSA_public_encryptRSA_private_decrypt等接口实现非对称加密和解密 (hayageek.com)。下文示例中,我们分别给出AES、SM4、DES/3DES和RSA的参考模型代码,并展示如何通过DPI将这些C++函数导入SystemVerilog环境。

一、C++参考模型代码示例

       以下示例代码使用OpenSSL的EVP接口和RSA接口实现加解密,并通过DPI-C暴露给SV调用。注意实际使用时需适当处理错误检查和内存管理,这里为示例简化处理流程。

// encryption_reference.cpp
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include "svdpi.h"// 辅助函数:打印OpenSSL错误(简化实现)
void print_openssl_error(const char* msg) {fprintf(stderr, "%s: %s\n", msg, ERR_error_string(ERR_get_error(), NULL));
}// AES-CBC 加密
extern "C" int aes_encrypt(svOpenArrayHandle plaintext_hdl, int pt_len,svOpenArrayHandle key_hdl, int key_len,svOpenArrayHandle iv_hdl,svOpenArrayHandle ciphertext_hdl) {// 将svOpenArrayHandle指针转换为unsigned char*unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);unsigned char* key = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* iv = (unsigned char*)svGetArrayPtr(iv_hdl);unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) return -1;// 使用AES-128-CBC作为示例;key_len假设为16(128位)if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {print_openssl_error("AES EncryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_EncryptUpdate(ctx, ct, &outl1, pt, pt_len)) {print_openssl_error("AES EncryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_EncryptFinal_ex(ctx, ct + outl1, &outl2)) {print_openssl_error("AES EncryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;  // 返回密文长度
}// AES-CBC 解密
extern "C" int aes_decrypt(svOpenArrayHandle ciphertext_hdl, int ct_len,svOpenArrayHandle key_hdl, int key_len,svOpenArrayHandle iv_hdl,svOpenArrayHandle plaintext_hdl) {unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);unsigned char* key = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* iv = (unsigned char*)svGetArrayPtr(iv_hdl);unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) return -1;if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {print_openssl_error("AES DecryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_DecryptUpdate(ctx, pt, &outl1, ct, ct_len)) {print_openssl_error("AES DecryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_DecryptFinal_ex(ctx, pt + outl1, &outl2)) {print_openssl_error("AES DecryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;
}// SM4-CBC 加密/解密(用mode区分,1=ECB,2=CBC)
extern "C" int sm4_crypt(svOpenArrayHandle in_hdl, int in_len,svOpenArrayHandle key_hdl,svOpenArrayHandle iv_hdl, int mode, int enc,svOpenArrayHandle out_hdl) {unsigned char* in = (unsigned char*)svGetArrayPtr(in_hdl);unsigned char* key = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* iv = (unsigned char*)svGetArrayPtr(iv_hdl);unsigned char* out = (unsigned char*)svGetArrayPtr(out_hdl);const EVP_CIPHER* cipher = NULL;if (mode == 1) {cipher = EVP_sm4_ecb();} else {cipher = EVP_sm4_cbc();}EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) return -1;if (enc) {if (!EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) {print_openssl_error("SM4 EncryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_EncryptUpdate(ctx, out, &outl1, in, in_len)) {print_openssl_error("SM4 EncryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_EncryptFinal_ex(ctx, out + outl1, &outl2)) {print_openssl_error("SM4 EncryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;} else {if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {print_openssl_error("SM4 DecryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_DecryptUpdate(ctx, out, &outl1, in, in_len)) {print_openssl_error("SM4 DecryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_DecryptFinal_ex(ctx, out + outl1, &outl2)) {print_openssl_error("SM4 DecryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;}
}// DES-EDE3 (3DES)加密
extern "C" int des3_encrypt(svOpenArrayHandle plaintext_hdl, int pt_len,svOpenArrayHandle key_hdl,svOpenArrayHandle iv_hdl, svOpenArrayHandle ciphertext_hdl) {unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);unsigned char* key = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* iv = (unsigned char*)svGetArrayPtr(iv_hdl);unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) return -1;// 3DES_EDE3_CBC,对应EVP_des_ede3_cbc()if (!EVP_EncryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, key, iv)) {print_openssl_error("3DES EncryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_EncryptUpdate(ctx, ct, &outl1, pt, pt_len)) {print_openssl_error("3DES EncryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_EncryptFinal_ex(ctx, ct + outl1, &outl2)) {print_openssl_error("3DES EncryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;
}// 3DES解密(类似AES-CBC解密步骤)
extern "C" int des3_decrypt(svOpenArrayHandle ciphertext_hdl, int ct_len,svOpenArrayHandle key_hdl,svOpenArrayHandle iv_hdl, svOpenArrayHandle plaintext_hdl) {unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);unsigned char* key = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* iv = (unsigned char*)svGetArrayPtr(iv_hdl);unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();if (!ctx) return -1;if (!EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, key, iv)) {print_openssl_error("3DES DecryptInit");EVP_CIPHER_CTX_free(ctx);return -1;}int outl1 = 0;if (!EVP_DecryptUpdate(ctx, pt, &outl1, ct, ct_len)) {print_openssl_error("3DES DecryptUpdate");EVP_CIPHER_CTX_free(ctx);return -1;}int outl2 = 0;if (!EVP_DecryptFinal_ex(ctx, pt + outl1, &outl2)) {print_openssl_error("3DES DecryptFinal");EVP_CIPHER_CTX_free(ctx);return -1;}EVP_CIPHER_CTX_free(ctx);return outl1 + outl2;
}// RSA加密(使用公钥加密/私钥解密)
// 假设传入的key_buf包含PEM格式的公钥文本
extern "C" int rsa_encrypt(svOpenArrayHandle plaintext_hdl, int pt_len,svOpenArrayHandle key_hdl, svOpenArrayHandle ciphertext_hdl) {unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);unsigned char* key_buf = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);BIO* bio = BIO_new_mem_buf(key_buf, -1);if (!bio) return -1;RSA* rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);BIO_free(bio);if (!rsa) {print_openssl_error("RSA create");return -1;}// 使用OAEP填充int len = RSA_public_encrypt(pt_len, pt, ct, rsa, RSA_PKCS1_OAEP_PADDING);RSA_free(rsa);if (len < 0) {print_openssl_error("RSA_public_encrypt");}return len;
}// RSA解密(使用私钥解密)
// 假设传入的key_buf包含PEM格式的私钥文本
extern "C" int rsa_decrypt(svOpenArrayHandle ciphertext_hdl, int ct_len,svOpenArrayHandle key_hdl, svOpenArrayHandle plaintext_hdl) {unsigned char* ct = (unsigned char*)svGetArrayPtr(ciphertext_hdl);unsigned char* key_buf = (unsigned char*)svGetArrayPtr(key_hdl);unsigned char* pt = (unsigned char*)svGetArrayPtr(plaintext_hdl);BIO* bio = BIO_new_mem_buf(key_buf, -1);if (!bio) return -1;RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);BIO_free(bio);if (!rsa) {print_openssl_error("RSA create");return -1;}int len = RSA_private_decrypt(ct_len, ct, pt, rsa, RSA_PKCS1_OAEP_PADDING);RSA_free(rsa);if (len < 0) {print_openssl_error("RSA_private_decrypt");}return len;
}

       上面代码示例中,svOpenArrayHandle用于接收SystemVerilog传来的动态数组(如byte [])。可以使用svGetArrayPtr(handle)将其转换为常规的unsigned char*指针来操作 (dvtalk.me)。例如,对于AES加密函数,我们首先创建一个EVP_CIPHER_CTX,调用EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)来设置AES-128-CBC加密上下文(其中key和iv需按实际长度提供 (tongsuo.readthedocs.io));然后调用EVP_EncryptUpdate处理输入明文,最后EVP_EncryptFinal_ex完成加密,得到输出密文长度。SM4加解密的流程与此类似,只需替换为EVP_sm4_cbc()等SM4算法对象 (docs.openssl.org)。对DES/3DES而言,可使用EVP_des_ede3_cbc()等作为Cipher类型进行加解密。RSA加密示例中,我们通过BIO_new_mem_buf将SystemVerilog传入的PEM格式公钥载入内存,然后用PEM_read_bio_RSA_PUBKEY得到RSA*结构,再调用RSA_public_encrypt(参考 (hayageek.com))。解密时则读入私钥并调用RSA_private_decrypt

二、DPI-C 导出及UVM调用

       上述C++函数编译后需作为共享库(SO/DLL)加载到仿真中,然后在SystemVerilog中用import "DPI-C"声明对应函数并调用。编译时需要加上OpenSSL库,例如示例Makefile可参考:

LDFLAGS = -Wl,-rpath,$(OPENSSL_PATH)/lib -lssl -lcrypto
CFLAGS = -fPIC -shared -I$(QUESTA_SIM_INS_DIR)/questasim/includeall: libcrypto_models.solibcrypto_models.so: encryption_reference.cppg++ $(CFLAGS) $(LDFLAGS) -o libcrypto_models.so encryption_reference.cpp

       上面使用了-fPIC -shared将代码编译为可加载共享库,同时链接了libssllibcrypto (dvtalk.me)。运行仿真时,需要在EDA工具的仿真选项(例如Modelsim/Questa的+sv_lib)中指定加载生成的libcrypto_models.so

       在SystemVerilog UVM测试代码中,需要类似如下方式导入C函数(以AES加密为例):

import "DPI-C" function int aes_encrypt(byte plaintext[], int pt_len,byte key[], int key_len,byte iv[], byte ciphertext[]);

       上述import语句的参数顺序和类型须与C++函数定义一致 (dvtalk.me)。调用时,SystemVerilog只需准备好byte类型的动态数组并传入,如:

byte pt_data[] = '{8'h01, 8'h23, 8'h45, 8'h67};
byte key[]     = '{8'h0f, 8'h47, ... }; // 16字节AES密钥
byte iv[16]    = '{default:'h00};
byte ct[64];  int ct_len;
ct_len = aes_encrypt(pt_data, pt_data.size(), key, key.size(), iv, ct);

       加密函数返回值为输出长度,ct数组长度需足够大以容纳输出 (dvtalk.me)。其他算法的导入方式类似,如sm4_crypt(...)des3_encrypt(...)rsa_encrypt(...)等均可通过类似的import "DPI-C"语句调用。

       通过上述流程,我们即可在UVM测试中调用OpenSSL参考模型,对AES、SM4、DES/3DES、RSA等平台进行加解密计算,作为硬件模块的Golden Reference,对比验证硬件结果的正确性 (tongsuo.readthedocs.io) (hayageek.com) (docs.openssl.org) (dvtalk.me)。

三、参考文献:

OpenSSL EVP加密文档 (tongsuo.readthedocs.io) (docs.openssl.org);RSA加密示例 (hayageek.com);SystemVerilog DPI-C使用示例 (dvtalk.me) (dvtalk.me)。

四、Learn more:

  1. EVP_EncryptInit
  2. EVP_sm4_cbc - OpenSSL Documentation
  3. RSA Encryption & Decryption Example with OpenSSL in C
  4. DPI example with AES-Openssl C-model | dvtalk
  5. DPI example with AES-Openssl C-model | dvtalk
  6. DPI example with AES-Openssl C-model | dvtalk
http://www.xdnf.cn/news/16767.html

相关文章:

  • BOSMA博冠8K广播级讯道摄像机DC0300 EFP产品画册(下)
  • 利用SQL文件上传注入植入WebShell
  • 【从零实践Onvif】01、Onvif详细介绍(从Onvif客户端开发的角度认识Onvif、Web Servies、WSDL、SOAP)
  • SpringCloud -- MQ高级
  • 数学建模——01规划/整数规划
  • Vue3 Composition API
  • Oracle迁移PostgreSQL隐式类型转换配置指南
  • RHCA - CL260 | Day03:配置 RHCS 集群
  • PyTorch 生态四件套:从图片、视频到文本、语音的“开箱即用”实践笔记
  • Git LFS 安装和使用
  • 动态规划Day5学习心得
  • 原生C++实现信号与槽机制:原理详解
  • windows环境下MySQL 8.0 修改或重置密码
  • SpringBoot 实现 RAS+AES 自动接口解密
  • 图像处理控件Aspose.Imaging教程:使用 C# 编程将 CMX 转换为 PNG
  • 基于 Rust 和土木工程、设备故障诊断、混凝土养护、GPS追踪、供应链物流跟踪系统、地下水监测等领域的实例
  • Y型M12一分二连接器:高效稳定的数据传输解决方案
  • 涿州周边水系分布三维地图
  • MyBatis Plus Wrapper 详细分析与原理
  • 代码随想录day50图论1
  • [leetcode] 反转字符串中的单词
  • Cockpit管理服务器
  • 在 CentOS 系统上安装 Docker
  • 《超级秘密文件夹》密码遗忘?试用版/正式版找回教程(附界面操作步骤)
  • NAT技术与代理服务
  • web服务器nginx
  • sqLite 数据库 (3):以编程方式使用 sqLite,4 个函数,以及 sqLite 移植,合并编译
  • USB电源原理图学习笔记
  • 相亲小程序聊天与互动系统模块搭建
  • 基于定制开发开源AI智能名片S2B2C商城小程序的B站私域流量引流策略研究