一、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);
}
我們先從函數的參數來看,只有r和ctx參數沒有被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的點滴,希望大家多批評指正
————————————————
版權聲明:本文為CSDN博主「artree_hao」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/nanshenyaohaohaode/article/details/79033644