[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/include

all: libcrypto_models.so

libcrypto_models.so: encryption_reference.cpp
    g++ $(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

元直数字电路验证

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值