mbedtls學習(8)ECDH密鑰協商

ECDH密鑰協商

ECDH密鑰協商算法基於橢圓曲線密碼系統(ECC),使用較短的密鑰長度可提供與RSA或DH算法同等的安全等級,密鑰長度位160 ~ 256比特的橢圓曲線算法與密鑰長度位1024 ~ 3072比特的非ECC算法安全強度相同。

常用有限域上的橢圓曲線

橢圓曲線由以下參數組成
T=(p,a,b,G,n,h)

  • p 有限域中的大素數,長度一般224比特、256比特、384比特
  • a 整數,橢圓方程係數
  • b整數,橢圓方程係數
  • G,橢圓曲線上某點,生成元
  • n,爲一個素數,表示橢圓曲線的階
  • h,餘因數
    其中G包含Gx和Gy 2個參數,非壓縮模式以04開始,壓縮模式以03開始,實際以非壓縮模式
    通過模數p和係數a,b構成橢圓曲線方程
    y^2=x^3+ax+b mod p
    TLS支持很多橢圓曲線,常用的有2個secp256r1和secp284r1

secp256r1
大素數p長度256比特(32字節)

p=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
a=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
b=5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
生成源G(Gx,Gy)
Gx=6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
Gy=4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
橢圓曲線的階n
n=FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551

secp384r1
大素數p長度384比特(48字節)

省略。。。
ECDH密鑰協商步驟

其實和DH思想一樣,只不過這是基於橢圓曲線離散對數上實現的。

  • ECDH共享參數
    Alice和Bob進行ECDH密鑰協商之前雙方要有共同的ECDH共享參數即必須選擇相同的橢圓曲線方程、大素數p、生成源G,實際中這些橢圓曲線已經被相關組織標準,比如上邊的secp256r1、secp384r1,通過這個雙方就確定了這些共享參數

  • ECHD密鑰協商
    1.Alice選擇一個比橢圓曲線階小的隨機數dA作爲私密參數,計算QA=dA G=(xA,yA)發送給Bob
    2.Bob選擇一個比橢圓曲線階小的隨機數dB作爲私密參數,計算QB=dB G=(xB,yB)發送給Alice
    3.Bob收到QA並計算得到共享密鑰參數KB=dB QA=dB(dA G)
    4.Alice收到QB並計算得到共享密鑰參數KA=dA QB=dA(dB G)
    根據橢圓曲線結合律dB(dA G) = dA(dB G) = (xQ,yQ),即獲得共享密鑰KA=KB,KA和KB是一個座標點,所以共享密鑰可以是xQ,yQ兩部分也可以是xQ單一部分,若共享曲線是secp256r1,則xQ,yQ長度均256比特,如共享曲線是secp384r1,則xQ,yQ長度均384比特
    如下圖
    在這裏插入圖片描述

mbedtls橢圓曲線模塊
  • ecp.c/ecp.h 橢圓曲線基本操作
  • ecp_curves.c 橢圓曲線定義
  • ecdh.c/ecdh.h 橢圓曲線密鑰協商
  • ecdsa.c/ecdsa.h 橢圓曲線數據簽名

mbedtls所支持的橢圓曲線可以通過config.h 中下面這些宏使能或者關閉,這些宏是ecp_curves.c中條件的編譯

/**
 * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED
 *
 * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve
 * module.  By default all supported curves are enabled.
 *
 * Comment macros to disable the curve and functions for it
 */
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
mbedtls ECHDH例子

先要打開這些宏,這個例子還是想DH協商那樣通過共享內存來模擬網絡交換共享參數。

#define MBEDTLS_AES_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ECP_C            開啓橢圓曲線基礎運算
#define MBEDTLS_ECDH_C           開啓橢圓曲線密鑰協商
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED 選擇secp256r1曲線參數
#define MBEDTLS_AES_ROM_TABLES
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include "mbedtls/ecdh.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/platform.h"

#define assert_exit(cond, ret) \
    do { if (!(cond)) { \
        printf("  !. assert: failed [line: %d, error: -0x%04X]\n", __LINE__, -ret); \
        goto cleanup; \
    } } while (0)
/*
static int entropy_source(void *data, uint8_t *output, size_t len, size_t *olen)
{
    uint32_t seed;
    seed = sys_rand32_get();
    if (len > sizeof(seed)) {
        len = sizeof(seed);
    }
    memcpy(output, &seed, len);
    *olen = len;
    return 0;
}*/

static void dump_buf(char *info, uint8_t *buf, uint32_t len)
{
    mbedtls_printf("%s", info);
    for (int i = 0; i < len; i++) {
        mbedtls_printf("%s%02X%s", i % 16 == 0 ? "\n     ":" ", 
                        buf[i], i == len - 1 ? "\n":"");
    }
}

int main(void)
{
    int ret = 0;
    size_t olen;
    char buf[65];
    mbedtls_ecp_group grp;
    mbedtls_mpi cli_secret, srv_secret;
    mbedtls_mpi cli_pri, srv_pri;
    mbedtls_ecp_point cli_pub, srv_pub;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    uint8_t *pers = "simple_ecdh";

    mbedtls_mpi_init(&cli_pri); //
    mbedtls_mpi_init(&srv_pri);
    mbedtls_mpi_init(&cli_secret); 
    mbedtls_mpi_init(&srv_secret);
    mbedtls_ecp_group_init(&grp); //初始化橢圓曲線羣結構體
    mbedtls_ecp_point_init(&cli_pub); //初始化橢圓曲線點結構體 cli
    mbedtls_ecp_point_init(&srv_pub);//初始化橢圓曲線點結構體 srv
    mbedtls_entropy_init(&entropy); //初始化熵結構體
    mbedtls_ctr_drbg_init(&ctr_drbg);//初始化隨機數結構體
/*
    mbedtls_entropy_add_source(&entropy, entropy_source, NULL,
                       MBEDTLS_ENTROPY_MAX_GATHER, MBEDTLS_ENTROPY_SOURCE_STRONG);*/
    mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, 
                                (const uint8_t *) pers, strlen(pers));
    mbedtls_printf("\n  . setup rng ... ok\n");

    //加載橢圓曲線,選擇SECP256R1
    ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
    mbedtls_printf("\n  . select ecp group SECP256R1 ... ok\n");
    //cli生成公開參數
    ret = mbedtls_ecdh_gen_public(&grp,    //橢圓曲線結構體
    							  &cli_pri,//輸出cli私密參數d
    							  &cli_pub,//輸出cli公開參數Q
                                  mbedtls_ctr_drbg_random, &ctr_drbg);
    assert_exit(ret == 0, ret);
    mbedtls_ecp_point_write_binary(&grp, &cli_pub, //把cli的公開參數到處到buf中
                            MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, buf, sizeof(buf));
    dump_buf("  1. ecdh client generate public parameter:", buf, olen);

	//srv生成公開參數
    ret = mbedtls_ecdh_gen_public(&grp,    //橢圓曲線結構體
    							  &srv_pri,//輸出srv私密參數d
    							  &srv_pub,//輸出srv公開參數Q
                                  mbedtls_ctr_drbg_random, &ctr_drbg);
    assert_exit(ret == 0, ret);
    mbedtls_ecp_point_write_binary(&grp, &srv_pub, //把srv的公開參數導出到buf中
                            MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, buf, sizeof(buf));
    dump_buf("  2. ecdh server generate public parameter:", buf, olen);
    //cli計算共享密鑰
    ret = mbedtls_ecdh_compute_shared(&grp,    //橢圓曲線結構體
                                      &cli_secret, //cli計算出的共享密鑰
                                      &srv_pub, //輸入srv公開參數Q
                                      &cli_pri, //輸入cli本身的私密參數d
                                      mbedtls_ctr_drbg_random, &ctr_drbg);
    assert_exit(ret == 0, ret);
	//把cli計算出的共享密鑰導出buf中
    mbedtls_mpi_write_binary(&cli_secret, buf, mbedtls_mpi_size(&cli_secret));
    dump_buf("  3. ecdh client generate secret:", buf, mbedtls_mpi_size(&cli_secret));

	//srv計算共享密鑰
    ret = mbedtls_ecdh_compute_shared(&grp,   //橢圓曲線結構體
                                      &srv_secret, //srv計算出的共享密鑰
                                      &cli_pub, //輸入cli公開參數Q
                                      &srv_pri, //輸入srv本身的私密參數d
                                      mbedtls_ctr_drbg_random, &ctr_drbg);
    assert_exit(ret == 0, ret);
	//把srv計算出的共享密鑰導出buf中
    mbedtls_mpi_write_binary(&srv_secret, buf, mbedtls_mpi_size(&srv_secret));
    dump_buf("  4. ecdh server generate secret:", buf, mbedtls_mpi_size(&srv_secret));

    //比較2個大數是否相等
    ret = mbedtls_mpi_cmp_mpi(&cli_secret, &srv_secret);
    assert_exit(ret == 0, ret);
    mbedtls_printf("  5. ecdh checking secrets ... ok\n");

cleanup:
    mbedtls_mpi_free(&cli_pri); 
    mbedtls_mpi_free(&srv_pri);
    mbedtls_mpi_free(&cli_secret); 
    mbedtls_mpi_free(&srv_secret);
    mbedtls_ecp_group_free(&grp);
    mbedtls_ecp_point_free(&cli_pub); 
    mbedtls_ecp_point_free(&srv_pub);
    mbedtls_entropy_free(&entropy); 
    mbedtls_ctr_drbg_free(&ctr_drbg);

    return 0;
}

運行結果

  . setup rng ... ok

  . select ecp group SECP256R1 ... ok
  1. ecdh client generate public parameter:
     04 D8 CC C3 43 FF 96 03 2A AB DD 1D 42 D3 D2 6E
     ED 31 08 EB 6D 6E D3 1E F0 E8 5A 8B 8E E3 2B D6
     02 B9 B3 AA 8F CF 99 EA 6E F5 3A 0B 04 34 4F 00
     6D B4 28 29 0E DF 1B 2B 38 E7 34 A7 E5 AD B4 6B
     1D
  2. ecdh server generate public parameter:
     04 5C C6 24 F1 61 E5 B2 B8 F1 45 73 3E 99 F8 1B
     DB 95 13 5E 9D 59 BA DA 7F 78 01 AC C8 3C FD 7E
     2F 27 04 18 B0 29 7F 9E C0 BF 86 78 CF 49 1C 98
     0B 03 75 B0 43 D0 36 F7 90 CF 54 49 8C F6 97 6D
     AF
  3. ecdh client generate secret:
     04 92 C4 4B 51 39 17 18 FB 6C 92 15 16 10 E6 D6
     B5 E4 CF 45 97 36 6C 12 22 E7 6E 83 02 C7 11 D9
  4. ecdh server generate secret:
     04 92 C4 4B 51 39 17 18 FB 6C 92 15 16 10 E6 D6
     B5 E4 CF 45 97 36 6C 12 22 E7 6E 83 02 C7 11 D9
  5. ecdh checking secrets ... ok

從運行結果看出

  • 模擬服務器和客戶端均產生了不同的橢圓曲線公鑰
  • 服務器和客戶端公鑰均採用非壓縮模式,因爲均以04開頭
  • 雙方採用secp256r1,所以生成公鑰長度都爲65字節,包括1字節壓縮提示,32字節x座標,32字節y座標
  • 最終雙方獲得相同的共享密鑰,共享密鑰僅包括x座標,所以長度是32字節
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章