micro-ecc [适用于MCU的ECC算法库]

适用于 8 位、32 位和 64 位处理器的小型且快速的 ECDH 和 ECDSA 实现。

nano-ecc是micro-ecc的分支,两者相比,nano-ecc针对Arduino适配,但没有汇编级优化,而micro-ecc针对乘法在ARM处理器中的实现做了汇编级优化。配合M0/M4处理器,可以更快实施ECC算法:ECDH/ECDSA。

1
https://github.com/kmackay/micro-ecc
1
https://github.com/iSECPartners/nano-ecc

算法介绍

ECDSA是用于数字签名,是ECC与DSA的结合,整个签名过程与DSA类似,所不一样的是签名中采取的算法为ECC,最后签名出来的值也是分为r,s。而ECC(全称Elliptic Curves Cryptography)是一种椭圆曲线密码编码学。

ECDH每次用一个固定的DH key,导致不能向前保密(forward secrecy),所以一般都是用ECDHE(ephemeral)或其他版本的ECDH算法。ECDH则是基于ECC的DH( Diffie-Hellman)密钥交换算法。

ECC与RSA 相比,有以下的优点:

  • 相同密钥长度下,安全性能更高,如160位ECC已经与1024位RSA、DSA有相同的安全强度。
  • 计算量小,处理速度快,在私钥的处理速度上(解密和签名),ECC远 比RSA、DSA快得多。
  • 存储空间占用小 ECC的密钥尺寸和系统参数与RSA、DSA相比要小得多, 所以占用的存储空间小得多。
  • 带宽要求低使得ECC具有广泛得应用前景。

在 ECDHE 密钥交换中,服务端使用证书私钥对相关信息进行签名,如果浏览器能用证书公钥验证签名,就说明服务端确实拥有对应私钥,从而完成了服务端认证。密钥交换和服务端认证是完全分开的。

可用于 ECDHE 数字签名的算法主要有 RSA 和 ECDSA,也就是目前密钥交换 + 签名有三种主流选择:

  • RSA 密钥交换(无需签名)
  • ECDHE 密钥交换、RSA 签名
  • ECDHE 密钥交换、ECDSA 签名;

移植说明

  • 注意移植随机数生成器,在文件: platform-specific.inc
  • 移植时注意平台定义,该库基于各个平台都做了一些优化,默认会自动选择平台,在 uECC.h 文件下定义 #define uECC_PLATFORM uECC_arch_other, 其中 uECC_arch_other 根据平台自己去适配

测试代码

注意测试代码需要使用 sha256 算法, 请参考其他 sha256 算法章节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>

#include "uECC.h"
#include "sha256.h"


void out_hex_string(char *info, uint8_t *buf, uint32_t size)
{
printf("%s : \r\n", info);
for (int i = 0; i < size; i++) {
printf("%02x ", buf[i]);
}
printf("\r\n");
}

#define ECDSA256_BYTES (32)
#define ECDSA256_PRIVATE_KEY_SIZE (ECDSA256_BYTES)
#define ECDSA256_PUBLIC_KEY_SIZE (ECDSA256_BYTES * 2)
#define ECDSA256_SIGNATURE_SIZE (ECDSA256_BYTES * 2)


int main(int argc, char **argv)
{
int ret;
int i, c;
int num_curves = 0;

uint8_t public_key[ECDSA256_PUBLIC_KEY_SIZE];
uint8_t private_key[ECDSA256_PRIVATE_KEY_SIZE];

char use_data[10] = "test";
uint8_t sha256[SHA256_DIGEST_SIZE];
uint8_t signature[ECDSA256_SIGNATURE_SIZE];

const struct uECC_Curve_t * curves[5];


#if uECC_SUPPORTS_secp160r1
curves[num_curves++] = uECC_secp160r1();
#endif
#if uECC_SUPPORTS_secp192r1
curves[num_curves++] = uECC_secp192r1();
#endif
#if uECC_SUPPORTS_secp224r1
curves[num_curves++] = uECC_secp224r1();
#endif
#if uECC_SUPPORTS_secp256r1
curves[num_curves++] = uECC_secp256r1();
#endif
#if uECC_SUPPORTS_secp256k1
curves[num_curves++] = uECC_secp256k1();
#endif

sha256_hash(use_data, strlen(use_data), sha256);
out_hex_string("sha256", sha256, sizeof(sha256));

uint32_t test_cnt = 0;

for (c = 0; c < num_curves; ++c) {
{
printf(".");
fflush(stdout);

if (!uECC_make_key(public_key, private_key, curves[c])) {
printf("uECC_make_key() failed\n");
return 1;
}
out_hex_string("private_key", private_key, uECC_curve_private_key_size(curves[c]));
out_hex_string("public_key", public_key, uECC_curve_public_key_size(curves[c]));

if (!uECC_sign(private_key, sha256, sizeof(sha256), signature, curves[c])) {
printf("uECC_sign() failed\n");
return 1;
}
out_hex_string("signature", signature, ECDSA256_SIGNATURE_SIZE);

if (!uECC_verify(public_key, sha256, sizeof(sha256), signature, curves[c])) {
printf("uECC_verify() failed\n");
return 1;
}

printf("uECC %d, test OK", c);

test_cnt++;
}
printf("\n");
}
return 0;
}