EVP简介

Openssl EVP提供了丰富的密码学中的各种函数。
Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。 EVP 函数将这些具体的算法进行了封装。

EVP 主要封装了如下功能函数:
1)实现了 base64 编解码 BIO;
2)实现了加解密 BIO;
3)实现了摘要 BIO;
4)实现了 reliable BIO;
5)封装了摘要算法;
6)封装了对称加解密算法;
7)封装了非对称密钥的加密(公钥)、解密(私钥)、签名与验证以及辅助函数;
7)基于口令的加密(PBE);
8)对称密钥处理;
9)数字信封:数字信封用对方的公钥加密对称密钥,数据则用此对称密钥加密。发送给对方时,同时发送对称密钥密文和数据密文。接收方首先用自己的私钥解密密钥密文,得到对称密钥,然后用它解密数据。
10)其他辅助函数。

EVP相关数据结构

EVP相关数据结构
声明在文件openssl-1.1.0c\include\opensslossl_typ.h
EVP相关算法上下文数据结构定义在文件openssl-1.1.0c\crypto\evp\evp_locl.h中。
EVP相关算法数据结构定义在文件openssl-1.1.0c\crypto\include\internal\evp_int.h中。

typedef struct evp_cipher_st EVP_CIPHER;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
typedef struct evp_md_st EVP_MD;
typedef struct evp_md_ctx_st EVP_MD_CTX;
typedef struct evp_pkey_st EVP_PKEY;

typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;

typedef struct evp_pkey_method_st EVP_PKEY_METHOD;
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;

typedef struct evp_Encode_Ctx_st EVP_ENCODE_CTX;

EVP_CIPHER

EVP_CIPHER用来存放对称加密相关的信息以及算法。

struct evp_cipher_st {
    int nid;  //对称算法 nid
    int block_size;  //对称算法每次加解密的字节数
    /* Default value for variable length ciphers */
    int key_len;  //对称算法的密钥长度字节数
    int iv_len;  //对称算法的填充长度
    /* Various flags */
    unsigned long flags;  //用于标记
    /* init key */
    /*加密初始化函数,用来初始化 ctx, key 为对称密钥值, iv 为初始化向量, enc用于指明是要加密还是解密,这些信息存放在 ctx 中*/
    int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,
                 const unsigned char *iv, int enc);
    /* encrypt/decrypt data */
    /*对称运算函数,用于加密或解密*/
    int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,
                      const unsigned char *in, size_t inl);
    /* cleanup ctx 清除上下文函数*/
    int (*cleanup) (EVP_CIPHER_CTX *);
    /* how big ctx->cipher_data needs to be */
    int ctx_size;
    /* Populate a ASN1_TYPE with parameters */
    /*设置上下文参数函数*/
    int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
    /* Get parameters from a ASN1_TYPE */
    /*获取上下文参数函数*/
    int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
    /* Miscellaneous operations 控制函数,实现各种其他操作*/
    int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr);
    /* Application data 用于存放应用数据*/
    void *app_data;
} /* EVP_CIPHER */ ;

对称算法上下文结构EVP_CIPHER_CTX ,此结构主要用来维护加解密状态,存放中间以及最后结果。因为加密或解密时,当数据很多时,可能会用到 Update 函数,并且每次加密或解密的输入数据长度任意的,并不一定是对称算法 block_size 的整数倍,所以需要用该结构来存放中间未加密的数据。

struct evp_cipher_ctx_st {
    const EVP_CIPHER *cipher;
    ENGINE *engine;             /* functional reference if 'cipher' is
                                 * ENGINE-provided */
    int encrypt;                /* encrypt or decrypt */
    int buf_len;                /* number we have left */
    unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
    unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
    unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
    int num;                    /* used by cfb/ofb/ctr mode */
    /* FIXME: Should this even exist? It appears unused */
    void *app_data;             /* application stuff */
    int key_len;                /* May change for variable length cipher */
    unsigned long flags;        /* Various flags */
    void *cipher_data;          /* per EVP data */
    int final_used;
    int block_mask;
    unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */
} /* EVP_CIPHER_CTX */ ;

openssl 对于各种对称算法实现了上述结构,各个源码位于 cypto/evp 目录下,文件名以 e_开头。 Openssl 通过这些结构来封装了对称算法相关的运算。

EVP_MD

EVP_MD结构用来存放摘要算法信息以及各种计算函数。

struct evp_md_st {
    int type;  //摘要类型,一般是摘要算法 NID
    int pkey_type;  //公钥类型,一般是签名算法 NID
    int md_size;  //摘要值大小,为字节数
    unsigned long flags;  //用于设置标记
    /*摘要算法初始化函数*/
    int (*init) (EVP_MD_CTX *ctx);
    /*多次摘要函数*/
    int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
    /*摘要完结函数*/
    int (*final) (EVP_MD_CTX *ctx, unsigned char *md);
    /*摘要上下文结构复制函数*/
    int (*copy) (EVP_MD_CTX *to, const EVP_MD_CTX *from);
    /*清除摘要上下文函数*/
    int (*cleanup) (EVP_MD_CTX *ctx);
    int block_size;
    int ctx_size;               /* how big does the ctx->md_data need to be */
    /* control function */
    int (*md_ctrl) (EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
} /* EVP_MD */ ;
struct evp_md_ctx_st {
    const EVP_MD *digest;
    /* functional reference if 'digest' is ENGINE-provided */
    ENGINE *engine;            
    unsigned long flags;
    void *md_data;
    /* Public key context for sign/verify */
    EVP_PKEY_CTX *pctx;
    /* Update function: usually copied from EVP_MD */
    int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
} /* EVP_MD_CTX */ ;

openssl 对于各种摘要算法实现了上述结构,各个源码位于 cypto/evp 目录下,文件名以 m_开头。 Openssl 通过这些结构来封装了各个摘要相关的运算。

EVP_PKEY

EVP_PKEY用来存放非对称密钥信息,可以是 RSA、 DSA、 DH 或 ECC 密钥。其中, ptr 用来存放密钥结构地址, attributes 堆栈用来存放密钥属性。

/*
 * Type needs to be a bit field Sub-type needs to be for variations on the
 * method, as in, can it do arbitrary encryption....
 */
struct evp_pkey_st {
    int type;
    int save_type;
    int references;
    const EVP_PKEY_ASN1_METHOD *ameth;
    ENGINE *engine;
    union {
        void *ptr;
# ifndef OPENSSL_NO_RSA
        struct rsa_st *rsa;     /* RSA */
# endif
# ifndef OPENSSL_NO_DSA
        struct dsa_st *dsa;     /* DSA */
# endif
# ifndef OPENSSL_NO_DH
        struct dh_st *dh;       /* DH */
# endif
# ifndef OPENSSL_NO_EC
        struct ec_key_st *ec;   /* ECC */
# endif
    } pkey;
    int save_parameters;
    STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
    CRYPTO_RWLOCK *lock;
} /* EVP_PKEY */ ;

EVP源码结构

evp 源码位于 crypto/evp 目录,可以分为如下几类:

1) 全局函数

主要包括 c_allc.c、 c_alld.c 以及 names.c。他们加载 openssl 支持的所有的对称算法和摘要算法,放入到哈希表中。实现了 OpenSSL_add_all_digests、
OpenSSL_add_all_ciphers 以及 OpenSSL_add_all_algorithms(调用了前两个函数)函数。在进行计算时,用户也可以单独加载摘要函数( EVP_add_digest)和对称计算函数( EVP_add_cipher)。

2) BIO 扩充

包括 bio_b64.c、 bio_enc.c、 bio_md.c 和 bio_ok.c,各自实现了 BIO_METHOD方法,分别用于 base64 编解码、对称加解密以及摘要。

3) 摘要算法 EVP 封装

由 digest.c 实现,实现过程中调用了对应摘要算法的回调函数。各个摘要算法提供了自己的 EVP_MD 静态结构,对应源码为 m_xxx.c。

4) 对称算法 EVP 封装

由 evp_enc.c 实现,实现过程调用了具体对称算法函数,实现了 Update 操作。
各种对称算法都提供了一个 EVP_CIPHER 静态结构,对应源码为 e_xxx.c。需要注意的是, e_xxx.c 中不提供完整的加解密运算,它只提供基本的对于一个 block_size数据的计算,完整的计算由 evp_enc.c 来实现。当用户想添加一个自己的对称算法
时,可以参考 e_xxx.c 的实现方式。一般用户至少需要实现如下功能:
- 构造一个新的静态的 EVP_CIPHER 结构;
- 实现 EVP_CIPHER 结构中的 init 函数, 该函数用于设置 iv,设置加解密标记、以及根据外送密钥生成自己的内部密钥;
- 实现 do_cipher 函数,该函数仅对 block_size 字节的数据进行对称运算;
- 实现 cleanup 函数,该函数主要用于清除内存中的密钥信息。

5) 非对称算法 EVP 封装

主要是以 p_开头的文件。其中, p_enc.c 封装了公钥加密; p_dec.c 封装了私钥解密; p_lib.c 实现一些辅助函数; p_sign.c 封装了签名函数; p_verify.c 封装了验签函数; p_seal.c 封装了数字信封; p_open.c 封装了解数字信封。

6) 基于口令的加密

包括 p5_crpt2.c、 p5_crpt.c 和 evp_pbe.c。