Python gmssl.SM2签名与验证的SystemVerilog集成示例

发布于:2025-07-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

Python gmssl.SM2签名与验证的SystemVerilog集成示例

       摘要:本文提供一个复杂的、完整的SM2签名与验证的SystemVerilog集成示例。这个例子将涵盖以下更复杂的场景:

  1. 密钥生成:在UVM测试开始时,动态调用Python生成SM2密钥对(公钥和私钥)。
  2. 数据签名:使用生成的私钥对一个随机消息进行签名。
  3. 签名验证:使用公钥、原始消息和签名进行验证。
  4. 数据交互:处理更复杂的数据结构,如公钥(一个点)、私钥(一个大数)和签名(两个大数r和s)在Python和SystemVerilog之间的传递。
  5. 正反例测试:包含一个验证成功的正向测试和一个验证失败(使用错误数据)的反向测试。

整体架构

依然采用 UVM <-> DPI-C <-> Python 的三层架构。


步骤 1: 创建Python Oracle (sm2_oracle.py)

       这个Python脚本将提供三个核心功能:gen_keys, sign, verify。我们使用argparse来管理不同的命令行模式,这比简单的sys.argv更健壮。

# sm2_oracle.py
import sys
import argparse
from gmssl.sm2 import sm2_crypt

# SM2标准中推荐的默认UserID
DEFAULT_USER_ID = '1234567812345678'

def gen_keys():
    """生成SM2密钥对并以hex格式打印到stdout"""
    sm2_key = sm2_crypt.gen_key()
    private_key = sm2_key.private_key.hex()
    # 公钥格式通常是 04 || x || y
    public_key = sm2_key.public_key.hex()
    # 使用逗号分隔私钥和公钥
    print(f"{private_key},{public_key}")

def sign(private_key_hex, data_hex):
    """使用私钥对数据进行签名"""
    try:
        data = bytes.fromhex(data_hex)
        signer = sm2_crypt.Sm2Crypt(private_key=private_key_hex)
        signature = signer.sign(data, DEFAULT_USER_ID.encode('utf-8'))
        print(signature.hex())
    except Exception as e:
        print(f"Error in signing: {e}", file=sys.stderr)
        sys.exit(1)

def verify(public_key_hex, data_hex, signature_hex):
    """使用公钥验证签名"""
    try:
        data = bytes.fromhex(data_hex)
        signature = bytes.fromhex(signature_hex)
        verifier = sm2_crypt.Sm2Crypt(public_key=public_key_hex)
        
        if verifier.verify(signature, data, DEFAULT_USER_ID.encode('utf-8')):
            print("PASS")
        else:
            print("FAIL")
    except Exception as e:
        print(f"Error in verification: {e}", file=sys.stderr)
        sys.exit(1)

def main():
    parser = argparse.ArgumentParser(description="SM2 Oracle for UVM via DPI-C")
    subparsers = parser.add_subparsers(dest='command', required=True)

    # gen_keys 命令
    parser_gen = subparsers.add_parser('gen_keys', help='Generate SM2 key pair')

    # sign 命令
    parser_sign = subparsers.add_parser('sign', help='Sign data with a private key')
    parser_sign.add_argument('--key', required=True, help='Private key in hex')
    parser_sign.add_argument('--data', required=True, help='Data to sign in hex')

    # verify 命令
    parser_verify = subparsers.add_parser('verify', help='Verify a signature')
    parser_verify.add_argument('--key', required=True, help='Public key in hex')
    parser_verify.add_argument('--data', required=True, help='Original data in hex')
    parser_verify.add_argument('--sig', required=True, help='Signature in hex')

    args = parser.parse_args()

    if args.command == 'gen_keys':
        gen_keys()
    elif args.command == 'sign':
        sign(args.key, args.data)
    elif args.command == 'verify':
        verify(args.key, args.data, args.sig)

if __name__ == "__main__":
    main()

步骤 2: 创建DPI-C桥接文件 (dpi_c_wrapper.c)

这个C文件提供三个独立的函数,分别对应Python脚本的三个功能。

// dpi_c_wrapper.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 通用的Python调用函数
static char* call_python_script(const char* command) {
    FILE *fp;
    // 使用一个足够大的静态缓冲区来存储结果
    static char result_buf[1024]; 

    fp = popen(command, "r");
    if (fp == NULL) {
        snprintf(result_buf, sizeof(result_buf), "ERROR_POPEN");
        return result_buf;
    }

    if (fgets(result_buf, sizeof(result_buf), fp) == NULL) {
        result_buf[0] = '\0'; // 读取失败则返回空字符串
    }
    
    pclose(fp);
    // 移除换行符
    result_buf[strcspn(result_buf, "\n")] = 0;
    return result_buf;
}

// 导出函数1: 生成密钥对
char* call_gmssl_sm2_gen_keys() {
    return call_python_script("python3 sm2_oracle.py gen_keys");
}

// 导出函数2: 签名
char* call_gmssl_sm2_sign(const char* private_key_hex, const char* data_hex) {
    char command[1024];
    snprintf(command, sizeof(command), "python3 sm2_oracle.py sign --key %s --data %s", private_key_hex, data_hex);
    return call_python_script(command);
}

// 导出函数3: 验证
char* call_gmssl_sm2_verify(const char* public_key_hex, const char* data_hex, const char* signature_hex) {
    char command[1024];
    snprintf(command, sizeof(command), "python3 sm2_oracle.py verify --key %s --data %s --sig %s", public_key_hex, data_hex, signature_hex);
    return call_python_script(command);
}

步骤 3: 编写UVM代码 (sm2_pkg.sv)

这个包里包含了事务、参考模型和测试用例。

// sm2_pkg.sv
package sm2_pkg;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    // 导入DPI-C函数
    import "DPI-C" function string call_gmssl_sm2_gen_keys();
    import "DPI-C" function string call_gmssl_sm2_sign(input string private_key_hex, input string data_hex);
    import "DPI-C" function string call_gmssl_sm2_verify(input string public_key_hex, input string data_hex, input string signature_hex);

    // 事务对象
    class sm2_transaction extends uvm_sequence_item;
        `uvm_object_utils(sm2_transaction)
        
        // SM2密钥长度为256位,公钥为点(x,y),签名也为(r,s)
        logic [255:0] private_key;
        logic [511:0] public_key; // 256-bit x, 256-bit y
        rand logic [255:0] message; // 为简化,我们直接对一个256位随机数签名
        logic [511:0] signature; // 256-bit r, 256-bit s
        bit verification_passed;

        function new(string name = "sm2_transaction");
            super.new(name);
        endfunction
    endclass

    // 参考模型
    class sm2_ref_model extends uvm_component;
        `uvm_component_utils(sm2_ref_model)
        function new(string name = "sm2_ref_model", uvm_component parent=null);
            super.new(name, parent);
        endfunction

        // 任务1: 生成密钥对
        task gen_keys(output logic [255:0] priv_key, output logic [511:0] pub_key);
            string key_pair_hex, priv_key_hex, pub_key_hex;
            int comma_pos;
            
            key_pair_hex = call_gmssl_sm2_gen_keys();
            
            // 在SystemVerilog中解析返回的 "priv,pub" 字符串
            comma_pos = key_pair_hex.find(",");
            if (comma_pos == 0) begin
                `uvm_fatal("REF_MODEL", "Failed to find comma in key_pair string from Python.")
                return;
            end
            
            priv_key_hex = key_pair_hex.substr(0, comma_pos - 1);
            // 公钥格式为 04 || x || y, gmssl返回的hex不含'04'前缀
            pub_key_hex = key_pair_hex.substr(comma_pos + 1, key_pair_hex.len() - 1);
            
            if ($sscanf(priv_key_hex, "%h", priv_key) != 1) `uvm_error("REF_MODEL", "Failed to parse private key");
            // 公钥是 512 bit (x,y),gmssl返回的hex正好是128个字符
            if ($sscanf(pub_key_hex, "%h", pub_key) != 1) `uvm_error("REF_MODEL", "Failed to parse public key");
        endtask

        // 任务2: 签名
        task sign(input logic [255:0] priv_key, input logic [255:0] msg, output logic [511:0] sig);
            string priv_key_s, msg_s, sig_s;
            priv_key_s = $sformatf("%064h", priv_key);
            msg_s = $sformatf("%064h", msg);
            sig_s = call_gmssl_sm2_sign(priv_key_s, msg_s);
            if ($sscanf(sig_s, "%h", sig) != 1) `uvm_error("REF_MODEL", "Failed to parse signature");
        endtask

        // 任务3: 验证
        task verify(input logic [511:0] pub_key, input logic [255:0] msg, input logic [511:0] sig, output bit passed);
            string pub_key_s, msg_s, sig_s, result_s;
            // 公钥需要去掉 '04' 前缀 (如果存在),但我们的Python脚本返回的是纯x,y拼接,所以直接用
            pub_key_s = $sformatf("%0128h", pub_key);
            msg_s = $sformatf("%064h", msg);
            sig_s = $sformatf("%0128h", sig);
            result_s = call_gmssl_sm2_verify(pub_key_s, msg_s, sig_s);
            passed = (result_s == "PASS");
        endtask
    endclass

    // 测试用例
    class base_test extends uvm_test;
        `uvm_component_utils(base_test)
        sm2_ref_model model;
        sm2_transaction tr;

        function new(string name = "base_test", uvm_component parent=null);
            super.new(name, parent);
        endfunction

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            model = sm2_ref_model::type_id::create("model", this);
            tr = sm2_transaction::type_id::create("tr");
        endfunction

        task run_phase(uvm_phase phase);
            phase.raise_objection(this);

            // --- 1. 正向测试: 签名并成功验证 ---
            `uvm_info(get_type_name(), "----------- Starting Positive Test Case -----------", UVM_LOW)
            // 生成密钥
            model.gen_keys(tr.private_key, tr.public_key);
            `uvm_info(get_type_name(), $sformatf("Generated PrivKey: %064h", tr.private_key), UVM_MEDIUM)
            `uvm_info(get_type_name(), $sformatf("Generated PubKey:  %0128h", tr.public_key), UVM_MEDIUM)
            
            // 随机化消息
            assert(tr.randomize(message));
            `uvm_info(get_type_name(), $sformatf("Message to sign:   %064h", tr.message), UVM_MEDIUM)

            // 签名
            model.sign(tr.private_key, tr.message, tr.signature);
            `uvm_info(get_type_name(), $sformatf("Generated Sig:     %0128h", tr.signature), UVM_MEDIUM)

            // 验证 (模拟DUT行为)
            model.verify(tr.public_key, tr.message, tr.signature, tr.verification_passed);
            
            if (tr.verification_passed)
                `uvm_info(get_type_name(), "SUCCESS: Signature verification PASSED as expected.", UVM_LOW)
            else
                `uvm_fatal(get_type_name(), "FAILURE: Signature verification FAILED unexpectedly.")
            
            // --- 2. 反向测试: 使用错误的消息进行验证 ---
            `uvm_info(get_type_name(), "----------- Starting Negative Test Case -----------", UVM_LOW)
            logic [255:0] wrong_message = ~tr.message;
            `uvm_info(get_type_name(), $sformatf("Verifying with WRONG message: %064h", wrong_message), UVM_MEDIUM)

            model.verify(tr.public_key, wrong_message, tr.signature, tr.verification_passed);
            
            if (!tr.verification_passed)
                `uvm_info(get_type_name(), "SUCCESS: Signature verification FAILED as expected.", UVM_LOW)
            else
                `uvm_fatal(get_type_name(), "FAILURE: Signature verification PASSED unexpectedly with wrong data.")

            phase.drop_objection(this);
        endtask
    endclass
endpackage

步骤 4: 编译和运行

创建 top.svMakefile

文件: top.sv

import uvm_pkg::*;
import sm2_pkg::*;

module top;
    initial begin
        run_test("base_test");
    end
endmodule

文件: Makefile

SIM = vcs

# 编译C代码为共享库
dpi_c_wrapper.so: dpi_c_wrapper.c
	gcc -shared -fPIC -o $@ $<

# 编译和仿真
run: dpi_c_wrapper.so
	$(SIM) -sverilog +v2k -full64 -debug_access+all \
	       -LDFLAGS "-Wl,--no-as-needed" \
	       top.sv sm2_pkg.sv \
	       -l dpi_c_wrapper.so
	./simv

clean:
	rm -rf simv* csrc ucli.key *.log *.so DVEfiles/

如何运行:

  1. sm2_oracle.py, dpi_c_wrapper.c, sm2_pkg.sv, top.sv, 和 Makefile 放在同一个目录下。
  2. 在终端中执行 make run

预期输出

你将看到类似下面的日志,清晰地展示了正向和反向测试的流程和结果:

UVM_INFO ... [base_test] ----------- Starting Positive Test Case -----------
UVM_INFO ... [base_test] Generated PrivKey: <64-char-hex>
UVM_INFO ... [base_test] Generated PubKey:  <128-char-hex>
UVM_INFO ... [base_test] Message to sign:   <64-char-hex>
UVM_INFO ... [base_test] Generated Sig:     <128-char-hex>
UVM_INFO ... [base_test] SUCCESS: Signature verification PASSED as expected.
UVM_INFO ... [base_test] ----------- Starting Negative Test Case -----------
UVM_INFO ... [base_test] Verifying with WRONG message: <64-char-hex>
UVM_INFO ... [base_test] SUCCESS: Signature verification FAILED as expected.

       这个更复杂的示例展示了如何在UVM环境中处理非对称加密的完整生命周期,包括动态生成密钥和处理更复杂的数据格式,为您构建强大的SM2验证环境提供了一个坚实的基础。


网站公告

今日签到

点亮在社区的每一天
去签到