学习ECC及Openssl下ECC生成密钥的部分源代码心得

一、ECC的简介

椭圆曲线算法可以看作是定义在特殊集合下数的运算,满足一定的规则。椭圆曲线在如下两个域中定义Fp域和F2m域。

  Fp域,素数域,p为素数;

  F2m域:特征为2的有限域,称之二元域或者二展域。域中,元素的个数为2m个。

    以下只介素数域。

    一些术语说明:

 1)  椭圆曲线的阶(order of a curve)

     椭圆曲线所有点的个数,包含无穷远点;

 2)  椭圆曲线上点的阶(order of a point)

      P为椭圆曲线上的点,nP=无穷远点,n取最小整数,既是P的阶;

 3)基点(base point)

      椭圆曲线参数之一,用G表示,是椭圆曲线上都一个点;

 4)余因子(cofactor)

      椭圆曲线的余因子,用h表示,为椭圆曲线点的个数/基点的阶

 5)椭圆曲线参数:

      素数域:(p,a,b,G,n,h)

      其中,p为素数,确定Fp,a和b确定椭圆曲线方程,G为基点,n为G的阶,h为余因子。

二、Openssl对ECC的实现

      Openssl实现ECC算法包括三部分:ECC算法(crypto/ec)、椭圆曲线数字签名算法ECDSA(crypto/ecdsa)以及椭圆曲线密钥交换算法ECDH(crypto/ecdh)。

2.1 数据结构

    我们首先来看一下密钥的数据结构(定义在crypto/ec/ec_lcl.h):

struct ec_key_st {
int version;//版本号
EC_GROUP *group;//密钥参数
EC_POINT *pub_key;//公钥
BIGNUM *priv_key;//大数私钥
//下面的是其他项
unsigned int enc_flag;
point_conversion_form_t conv_form;
int references;
int flags;
EC_EXTRA_DATA *method_data;
};

     初次学习看到这个一头雾水,但没有关系我们一个个来分析,从源代码中找到源头。

     首先让我们疑惑的应该是EC_GROUP结构体(crypto/ec/ec_lcl.h):

struct ec_group_st {
    const EC_METHOD *meth; //方法(函数)
    EC_POINT *generator;        //基向量(基点)
    BIGNUM order, cofactor;  //基点的阶和余因子
    int curve_name;      //选择椭圆曲线的名字     
BIGNUM a, b;//a和b确定椭圆曲线方程
//其他项此处省去
};

     这里可以看到EC_GROUP结构体包含了一个椭圆曲线的基本参数,是不是感到豁然开朗,等等,EC_METHOD结构体又是什么?

     别着急,我们再来找一下EC_METHOD在哪里定

     EC_METHOD结构体定义如下(crypto/ec/ec_lcl.h

struct ec_method_st {
    int flags;
    int field_type;           
    int (*group_init) (EC_GROUP *);
    void (*group_finish) (EC_GROUP *);
    void (*group_clear_finish) (EC_GROUP *);
int (*group_copy) (EC_GROUP *, const EC_GROUP *);
//其他函数声明此处省去
};

     当打开源代码看到EC_METHOD构体,映入眼帘的是大段大段的函数声明,由此可以想构体是用来封装椭圆曲线的一些基本操作的,不用着急等我需要的候再来一个个看。

    最后要介的是点的数据构:

     

struct ec_point_st {
    const EC_METHOD *meth;
    BIGNUM X;
    BIGNUM Y;
    BIGNUM Z;              
    int Z_is_one;              
} 
    

    这个数据结构比较简单了定义了三维坐标X,Y,Z和方法。

    当看到里,于可以松一口气了一下密的数据主要包含了私钥(大数)、向量)、和EC_GROUP结构体。而EC_GROUP结构体包含了椭圆曲线的参数和EC_METHOD结构体,而EC_METHOD结构体是一些椭圆线运算操作函数的封装。

    这还没完,的数据构中是不是有一个BIGNUM结构体我们不认识。新的学习历程又要开始了。

    Openssl的大数表示用BIGNUM结构体,定下(crypto/bn/bn.h)

struct bignum_st {
    BN_ULONG *d;               
    int top;                  
    int dmax;                  
    int neg;                   
    int flags;
}; 
      

     d:BN_ULONG首地址,大数存放在里面。(BN_ULONG可能是被声明一种基本型如int名,本人没找到在哪被声明的,希望各位予指正

     top:用来指明大数占多少个BN_ULONG

     dmax:d的大小。

     neg:是否为负数,如果为1数,为0则为正数。

    flags:用于存放一些标记,用于区分配和动态分配。

    读到这终于对密钥结构体有一个全面的认识了。


2.2 密钥生成源代码

     接下来我们进入文章的主题-密钥的生成。

    椭圆曲线的密钥生成实现在crytpo/ec/ec_key.c中。Openssl中,椭圆线生成,首先用需要取一种椭圆线(openssl的crypto/ec_curve.c中内置实现67种,EC_get_builtin_curves获取该列表),然后根据选择椭圆线计算密生成参数group,最后根据密参数group来生公私

     我先来找到密生成的源代码,段函数之前已经选择椭圆线,并生成完参数group,并且将group赋值给了密钥结构eckey了,我把我所能理解的内容尽量注在每行代之后。

int EC_KEY_generate_key(EC_KEY *eckey)
{
    int ok = 0;//判断是否成功
BN_CTX *ctx = NULL;// BN_CTX是在大数那部分定义的一个上下文情景函数,用来存储计算中的中间过程。
//初始化参数
    BIGNUM *priv_key = NULL, *order = NULL;
    EC_POINT *pub_key = NULL;

#ifdef OPENSSL_FIPS//如果宏定义了OPENSSL_FIPS 则执行下述语句
    if (FIPS_mode())
        return FIPS_ec_key_generate_key(eckey);
#endif
    //判断密钥以密钥生产参数是否为空
    if (!eckey || !eckey->group) {
        ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
        return 0;
    }
    // 分配空间初始化
    if ((order = BN_new()) == NULL)
        goto err;
    if ((ctx = BN_CTX_new()) == NULL)
        goto err;
   //给priv_key赋初值
    if (eckey->priv_key == NULL) {
        priv_key = BN_new();
        if (priv_key == NULL)
            goto err;
    } else
        priv_key = eckey->priv_key;
    //此函数的作用是从group参数中提取椭圆曲线的阶
    if (!EC_GROUP_get_order(eckey->group, order, ctx))
        goto err;
     //为priv_key选取随机数,随机数的范围是两者之间。
    do
        if (!BN_rand_range(priv_key, order))
            goto err;
    while (BN_is_zero(priv_key)) ;
   
    //给pub_key分配存储空间
    if (eckey->pub_key == NULL) {
        pub_key = EC_POINT_new(eckey->group);
        if (pub_key == NULL)
            goto err;
    } else
        pub_key = eckey->pub_key;
    //这个函数的用途是用来计算点乘,输入的参数依次是椭圆曲线生产参数、运算结果保存处pub_key、大数私钥、null、null和上下文情景函数。
    if (!EC_POINT_mul(eckey->group, pub_key, priv_key, NULL, NULL, ctx))
        goto err;

    eckey->priv_key = priv_key;
    eckey->pub_key = pub_key;

    ok = 1;
//错误处理
 err:
    if (order)
        BN_free(order);
    if (pub_key != NULL && eckey->pub_key == NULL)
        EC_POINT_free(pub_key);
    if (priv_key != NULL && eckey->priv_key == NULL)
        BN_free(priv_key);
    if (ctx != NULL)
        BN_CTX_free(ctx);
    return (ok);
}
     读到这是否感觉其实椭圆曲线生产密钥的源代码很简单?等等,我们好像错过了一个重要函数的实现---- EC_POINT_mul(eckey->group, pub_key, priv_key, NULL , NULL ,ctx)   

       这个函数定义在crytpo/ec/ec_lib.c

int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx)
{
    const EC_POINT *points[1];
    const BIGNUM *scalars[1];
    points[0] = point;
    scalars[0] = p_scalar;
    return EC_POINTs_mul(group, r, g_scalar, (point != NULL
&& p_scalar != NULL), points, scalars, ctx);
}

     先从函数的参数来看,只有rctx参数没有被const所限制,其他值在此函数运算中不能被改ctx好理解,就是相当于一个记录日志。而r就是此函数运算的果。用函数表示为:

   r = g_scalar * G + p_scalar * point   

      G为此椭圆曲线的基向量,从曲线参数group

      此函数用了多点相乘函数EC_POINTs_mul个函数被定在相同文件下

     

*scalar,size_t num, const EC_POINT *points[],const BIGNUM *scalars[], BN_CTX *ctx)
{
    if (group->meth->mul == 0)//判断是多点还是单点
    return ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx);
    return group->meth->mul(group, r, scalar, num, points, scalars, ctx);
}
    

    这个函数输入的几个参数与前一个函数差不多,只是从个点成了多个点。

    我想从简单的对单点乘来分析,但是找不到源代码在哪,若哪位专业人士知道在哪,恳请留下路径,谢谢谢谢

    历时四天还处openssl的初级阶段,是个小白,使用为macOS 10.13.1,初次使用时连crypto、demos等目都找不到,发现编译后就成了libcrypto.a态链文件,只能用很笨的方法,在编译过程中止操作,就保留了crypto、demos等目,从里的源代开始学。以后的学方法也要慢慢改。初次记录习openssl的点滴,希望大家多批评指正。


  

猜你喜欢

转载自blog.csdn.net/nanshenyaohaohaode/article/details/79033644
今日推荐