找回密码
 立即注册
首页 业界区 业界 secp256k1算法详解一

secp256k1算法详解一

鞠彗云 2025-6-20 16:33:55
1 简介

⽐特币基于椭圆曲线加密的椭圆曲线数字签名算法(ECDSA),特定的椭圆曲线称为secp256k1。其公式定义如下
y2=x3+ax+b mod p
其中:p = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F = 2256-232-29-28-27-26-24-1 = 2256-232-977,a = 0, b=7
基点G为:x=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,y=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
G的阶为:n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
secp256k1的命名出自一个密码协议标准,每一个字母和数字都代表着特定含义,下面分别进行介绍
1.png

1.1 密码协议标准

第一部分是「sec」,sec是Standards for Efficient Cryptography 的简称,是SECG发布的一种密码学协议标准。SECG发布的「SEC 1」和「SEC 2」两个关于椭圆曲线的协议标准,在「SEC 2」中有详细说明secp256k1以及其他曲线的参数定义。除了「sec」,还有众多其他关于椭圆曲线的协议标准,从SafeCurve中可以看到有下列不同类型的标准。
2.png

「SafeCurve」此处较久没有更新,有些标准已经更新了多次,例如NIST关于数字签名的标准 FIPS 186目前在用的是第四版,第五版也在起草中了,从「NIST」官网中可见。
1.2 有限域

第二部分是「p」,p表示该椭圆曲线是基于素数有限域Fp。有限域是离散数学中的概念,它是一个由有限数量元素组成的集合,元素之间可以进行加法和乘法计算。密码学中使用椭圆曲线都是基于有限域的,除了素数有限域Fp之外,还有另一种特征为2的有限域F2m,Fp的大小(元素个数)为p,F2m的大小为2m。基于Fp的椭圆曲线为:
3.png

基于F2m的椭圆曲线为:
4.png

在「SEC 2」中还定义了sect163k1、sect163r1等曲线,其中,t表示的是该曲线基于F2m。在「NIST FIPS 186-4」中定了P-256、B-163等曲线,P-表示基于Fp,B-表示基于F2m。
1.3 有限域大小 

每个椭圆曲线E都有若干关键参数,包括阶为n的基点G和协因子h等,其中,n为一个大素数,n*h为椭圆曲线上点的数量。为了计算效率考虑,h通常设置为1、2或4。通俗地讲,如果椭圆曲线上的点数量越多,那么这条椭圆曲线的安全度就越高,因此n的取值是影响曲线安全的关键。椭圆曲线又都是基于有限域的,曲线上的点都是有限域中的元素,因此,有限域大小决定了曲线安全度。第三部分「256」就是有限域大小的表现形式,还有更多其他如192、224、384等,在「NIST FIPS 186-4」中有个表格展现了Fp 和F2m两个域的各种不同大小配置。
5.png

SEC标准在这块的设置和NIST标准类似,我们会看到p系列的曲线有p192、p224、p256(secp256k1就是其中一种)、p384和p521,t/B系列有t163/B-163、t233/B-233等。
1.4 Koblitz Curve

第四部分「k」表示该曲线是Koblitz Curve(科布利兹曲线),从「SEC 2」中可以看到还有此处标记为r的曲线(如secp256r1),r表示该曲线是伪随机曲线Pseudo-Random Curve。Koblitz Curve命名源自数学家「Neal Koblitz」,它是一种特殊的曲线,它的一些参数是精心挑选设置的。Koblitz Curve具有自同态的性质,可以通过优化大幅提升计算效率。相比之下,Pesudo-Random Curve的对应参数是通过随机种子计算出来的,有标准的检验算法可以检测所有参数是随机种子产生而来。在「NIST FIPS 186-4」中Koblitz Curve曲线以「K-」标记开头,分别有K-163、K-233等。
1.5 末位标记

到了第五部分「1」,这是代表在前4个条件下提供了多种推荐参数设置,在SEC标准中大部分该位都是1,即只提供一种推荐参数,sect163r2是一个例外。下面把SEC和NIST两个标准推荐的曲线分别列一下,二者有较大部分是相同的参数设置。
6.png

上述表格中,同一行中SEC和NIST都出现的,两个曲线虽然名字不同,但参数完全相同,也就是说其实一样的。橙色底纹的几个SEC曲线没有对应的NIST曲线,因此SEC标准包含的曲线比NIST多一些,如这里secp256k1就是SEC标准单独存在的。说到这里,不得不提一个正经八卦。据说,NIST推荐的Pesudo-Random Curve,也就是P和B系列,并没有公布随机数挑选规则,外界存在一种疑虑,可能NSA(美国国家安全局)掌握了后门,能够轻易破解这些密码协议。
2  源码及编译

secp256k1源码可以从以下地址下载:https://github.com/bitcoin-core/secp256k1
用git下载源码
  1. git clone https://github.com/bitcoin-core/secp256k1
复制代码
Check out最新release版本
  1. git checkout v0.6.0
复制代码
2.1 Linux下编译

在Linux下可以使用Autotools进行编译
  1. $ ./autogen.sh       # Generate a ./configure script
  2. $ ./configure        # Generate a build system
  3. $ make               # Run the actual build process
  4. $ make check         # Run the test suite
  5. $ sudo make install  # Install the library into the system (optional)
复制代码
可以用./configure --prefix=指定安装路径,如我将编译结果安装到buildout目录下,其目录结构如下:
7.png

 
2.2 Windows编译

在Windows下,使用CMake+VS2019进行编译,编译配置如下:
8.png

Generate时使用默认配置,完成后用VS2019打开生成好的工程,进行编译,结果如下:

运行其中的tests项目输出如下:
10.png

由于内容比较多,所以运行花费时间较长。
3 应用

用VS2019创建控制台应用程序secp256k1Test,并将include和之前编译生成lib库及dll库放到工程目录下
11.png

按照以上目录结构,修改项目C/C++中的包含路径及链接器中的配置
12.png

 
主程序secp256k1Test.c如下
13.gif
14.gif
  1. 1 #include <stdlib.h>
  2. 2 #include <stdio.h>
  3. 3 #include <string.h>
  4. 4 #include <stdint.h>
  5. 5 #include "secp256k1.h"
  6. 6
  7. 7 #define    bswap_16(value)  \
  8. 8     ((((value) & 0xff) << 8) | ((value) >> 8))
  9. 9
  10. 10 #define    bswap_32(value)    \
  11. 11     (((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \
  12. 12     (uint32_t)bswap_16((uint16_t)((value) >> 16)))
  13. 13
  14. 14 #define    bswap_64(value)    \
  15. 15     (((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) \
  16. 16     << 32) | \
  17. 17     (uint64_t)bswap_32((uint32_t)((value) >> 32)))
  18. 18
  19. 19 int main()
  20. 20 {
  21. 21     int ret;
  22. 22     secp256k1_context* pCtx = NULL;
  23. 23     secp256k1_pubkey pubkey;
  24. 24     unsigned char rand32[32] = { 0 };
  25. 25     printf("this is for secp256k1 testing\n");
  26. 26     
  27. 27     pCtx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
  28. 28     if (pCtx) {
  29. 29         for (int i = 0; i < sizeof(rand32) / 4; i++) {
  30. 30             int r = rand();
  31. 31             memcpy(rand32 + i * 4, &r, 4);
  32. 32         }
  33. 33         if (!secp256k1_context_randomize(pCtx, rand32))
  34. 34             printf("secp256k1_context_randomize failed\n");
  35. 35         else {
  36. 36             printf("secp256k1_context_randomize success\n");
  37. 37         }
  38. 38
  39. 39         memset(rand32, 0, sizeof(rand32));
  40. 40         rand32[31] = 1;
  41. 41         ret = secp256k1_ec_pubkey_create(pCtx, &pubkey, rand32);
  42. 42         if (ret) {
  43. 43             printf("private key\n0x");
  44. 44             for (int i = 0; i < 32; i++)
  45. 45                 printf("%02x", rand32[i]);
  46. 46             printf("\n");
  47. 47             printf("secp256k1_ec_pubkey_create success\n");
  48. 48             printf("0x");
  49. 49             for (int i = 0; i < 32; i++)
  50. 50                 printf("%02x", pubkey.data[31-i]);
  51. 51             printf("\n0x");
  52. 52             for (int i = 0; i < 32; i++)
  53. 53                 printf("%02x", pubkey.data[63-i]);
  54. 54             printf("\n");
  55. 55         }
  56. 56         else {
  57. 57             printf("secp256k1_ec_pubkey_create failed\n");
  58. 58         }
  59. 59
  60. 60         // order N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
  61. 61         unsigned int* pData = (unsigned int*)rand32;
  62. 62         pData[0] = bswap_32(0xFFFFFFFF);
  63. 63         pData[1] = bswap_32(0xFFFFFFFF);
  64. 64         pData[2] = bswap_32(0xFFFFFFFF);
  65. 65         pData[3] = bswap_32(0xFFFFFFFE);
  66. 66         pData[4] = bswap_32(0xBAAEDCE6);
  67. 67         pData[5] = bswap_32(0xAF48A03B);
  68. 68         pData[6] = bswap_32(0xBFD25E8C);
  69. 69         pData[7] = bswap_32(0xD0364140);
  70. 70         ret = secp256k1_ec_pubkey_create(pCtx, &pubkey, rand32);
  71. 71         if (ret) {
  72. 72             printf("private key\n0x");
  73. 73             for (int i = 0; i < 32; i++)
  74. 74                 printf("%02x", rand32[i]);
  75. 75             printf("\n");
  76. 76             printf("secp256k1_ec_pubkey_create success\n");
  77. 77             printf("0x");
  78. 78             for (int i = 0; i < 32; i++)
  79. 79                 printf("%02x", pubkey.data[31 - i]);
  80. 80             printf("\n0x");
  81. 81             for (int i = 0; i < 32; i++)
  82. 82                 printf("%02x", pubkey.data[63 - i]);
  83. 83             printf("\n");
  84. 84         }
  85. 85         else {
  86. 86             printf("secp256k1_ec_pubkey_create failed\n");
  87. 87         }
  88. 88
  89. 89         secp256k1_context_destroy(pCtx);
  90. 90     }
  91. 91     
  92. 92     return 1;
  93. 93 }
复制代码
secp256k1Test.c程序第40,41行,以0x1为私钥产生公钥,即椭圆曲线生成元G。程序第62~70行, 以阶n-1为私钥产生公钥,即(n-1)*G,这里仅为测试,所以两次取的私钥都是特殊值,正常情况下私钥要随机产生,程序最终运行结果如下
  1. this is for secp256k1 testing
  2. secp256k1_context_randomize success
  3. private key
  4. 0x0000000000000000000000000000000000000000000000000000000000000001
  5. secp256k1_ec_pubkey_create success
  6. 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
  7. 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
  8. private key
  9. 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140
  10. secp256k1_ec_pubkey_create success
  11. 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
  12. 0xb7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777
复制代码
有椭圆曲线理论可知两次产生的公钥点互为逆元,它们关于x轴对称,x坐标相同,y坐标互为相反数(在有限域内y坐标之和为模数p)。
15.png

参考:
https://cloud.tencent.com/developer/news/586021

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册