• [原]替换OpenSSL Engine为硬件加密卡之替换EVP_CIPHER结构


    定义:
        static const EVP_CIPHER FMC_ENG_evp_cipher=
        {
            NID_aes_128_cbc,                /*nid*/
            16,                                /*block_size*/
            16,                                /*key_len*/
            16,                                /*iv_len*/
            EVP_CIPH_CBC_MODE,                /*Various flags*/
            FMC_ENG_evp_cipher_init,        /*init*/
            FMC_ENG_evp_cipher_do_cipher,    /*do_cipher*/
            FMC_ENG_evp_cipher_cleanup,        /*cleanup*/
            sizeof(AES_KEY) + 16,            /*ctx_size*/
            NULL,                            /*set_asn1_parameters*/
            NULL,                            /*get_asn1_parameters*/
            NULL,                            /*Miscellaneous operations*/
            NULL                            /*app_data*/
        };
        首先解释:
        NID_aes_128_cbc 为算法NID, 在bind engine时, 有调用:
        ret = ENGINE_set_ciphers(e, FMC_ENG_ciphers);
        // 参见: crypto\engine\eng_cryptodev.c  line:608
        static int

        FMC_ENG_ciphers(ENGINE *e, const EVP_CIPHER **cipher, const int **nids, int nid)
        {   
            if(cipher == NULL) // yes, refer to get_cryptodev_ciphers
            {
                *nids = FMC_ENG_cipher_nids;
                return (sizeof(FMC_ENG_cipher_nids)-1)/sizeof(FMC_ENG_cipher_nids[0]);
            }

            switch (nid)
            {
            case NID_aes_128_cbc:
                *cipher = FMC_ENG_get_evp_cipher();
                break;           
            default:           
                *cipher = NULL;
                break;
            }
            return (*cipher != NULL);
        }
        const EVP_CIPHER *FMC_ENG_get_evp_cipher(void)
        {   
            return(&FMC_ENG_evp_cipher);
        }
        在调用EVP_CIPHER* ciphter = (EVP_CIPHER *)(EVP_aes_128_cbc());
        时, FMC_ENG_ciphers函数会被调用, 就在此时, 返回我们自己定义的EVP_CIPHER
        结构.
       
        EVP测试代码调用过程如下:
       
        ciph_ctx = EVP_CIPHER_CTX_new();
        _ASSERT(ciph_ctx != NULL);
        //EVP_CIPHER_CTX_init(ciph_ctx); // new后会自动调用init - memset(0)
       
        ret = EVP_EncryptInit_ex(ciph_ctx, cipher, engine, aes_key, aes_iv);
        if (ret != 1) {       
            return -1;
        }
        ret = EVP_EncryptUpdate(ciph_ctx, aes_out, &upd_outlen, pin, pin_len);
        if (ret != 1) {       
            return -2;
        }
        ret = EVP_EncryptFinal(ciph_ctx, aes_out + upd_outlen, &upd_outlen);
        if (ret != 1) {       
            return -3;
        }
       
        EVP_CIPHER_CTX_free(ciph_ctx);
       
       
        EVP_EncryptInit_ex会调用到FMC_ENG_evp_cipher_init
        EVP_EncryptUpdate和EVP_EncryptFinal会调用到FMC_ENG_evp_cipher_do_cipher
        EVP_CIPHER_CTX_free会调用到EVP_CIPHER_CTX_cleanup->FMC_ENG_evp_cipher_cleanup
       
        中间遇到几个问题:
        1 Update不能如此调用:
        while(pin_len > 0) {

            ret = EVP_EncryptUpdate(&ciph_ctx, aes_out + aes_etotal, &upd_outlen, pin, pin_len);
            if (ret != 1) {   
                break;
            } else {
                // yes, correct encypt next block
            }       
           
            aes_etotal += upd_outlen;

            // 后面的属于画蛇添足
            _ASSERT(aes_etotal <= aes_outlen);

            if(upd_outlen == 0) { // all block_size aligned block completed.                                   
                break;
            }

            if(upd_outlen >= pin_len) {
                pin_len = 0;           
                break;    // all encryptupdate completed
            } else {           
                pin_len -= upd_outlen;
                pin += upd_outlen;   
            }       
        }
       
        如果这样, 测试时, plaintext数据长度为0x33, 第一次update后, 返回已加密
        长度为0x30, 接着调update, 就有3个字节被放到了ctx->buf中, 返回的update
        长度为0.
        然后调用Final函数, 如此:
        ret = EVP_EncryptFinal(&ciph_ctx, aes_out + aes_etotal, &upd_outlen);
        这样, 又有3个字节会被加入到ctx->buf中, 最后被拷贝到ctx->final buffer
        中, 执行padding方案后, 被加密, 整个加密的长度变成了0x36.
       
        因为测试时, 用硬件Engine和Openssl只带Engine的方式一样, 所以加密出来的
        数据一样, 通过检测. 但解密后数据长度为0x36个字节, 晕菜. 被自己摆了一道.
       
        2 ctx_size
        开始时搞不明白FMC_ENG_evp_cipher::ctx_size是用来干什么的,    后来搞明白了.
        其实这里不用像openssl那样定义, openssl是在EncryptInit是, 按照这个大小,
        分配了一个AES_KEY+x个字节的memory, 用来存放EncryptInit是用用户输入的
        key产生一个aes key(包含n个roundtable,roundtable用来在aes加密是进行置换,
        aes的核心就是置换和移位). 我们的硬件引擎之需要分配ctx->cipher->key_size
        个大小的内存, memcpy key到里面即可, 在do_cipher时, key就从里面取出.
        忘记写了, 分配的内存地址赋值给ctx->cipher_data指针.
       
        3 padding
        在想如何替换Engine时, 主要围绕硬件加密卡提供的API进行考虑, 首先想到的
        就是padding方案. 因为硬件加密卡要求输入的数据必须是按照block_size对齐的
        openssl的evp函数是否会自动进行padding呢? 答案是 - yes.
       
        如: 在输入数据为0x33长度是, update加密, 先加密前面0x30个, 执行final时
        会执行padding方案, 此时ctx->final_used标识会被置1. 调用do_cipher时,
        传入的数据已经是按照block_size对齐的了.
       
        OpenSSL的Padding方案:
        差几个对齐, payload后面就填几, 如果对齐了, 就加一个完整的block.
        所以, 加密出来的数据, 可能会比输入数据多一个block, 在分配ciphertext的
        buffer时, 需要注意.
       
        4 编译优化
        为了看openssl的padding方案, 跟到openssl的代码中去, 发现老是符号与代码
        不匹配, 还以为自己不小心动到了openssl的代码, 反复几次重新编译openssl
        均不能解决问题. 百思不得其解, 后trace到汇编里面, 发现在指定padding方案
        时, for(n=bl; n<b; n++) out[n] = n; 被优化成memset(out+n, n, b-n);
        原来是openssl的编译mak文件中, 指定了Ox优化编译选项, 将该选项改为Od,
        重新编译, OK.

  • 相关阅读:
    LeetCode 252. Meeting Rooms
    LeetCode 161. One Edit Distance
    LeetCode 156. Binary Tree Upside Down
    LeetCode 173. Binary Search Tree Iterator
    LeetCode 285. Inorder Successor in BST
    LeetCode 305. Number of Islands II
    LeetCode 272. Closest Binary Search Tree Value II
    LeetCode 270. Closest Binary Search Tree Value
    LeetCode 329. Longest Increasing Path in a Matrix
    LintCode Subtree
  • 原文地址:https://www.cnblogs.com/crunchyou/p/2867735.html
Copyright © 2020-2023  润新知