椭圆曲线密码学C语言实现解密异常求助:疑点减法错误
解决ECC解密失败的问题
我仔细看了你的代码,确实有几个关键细节出了问题,导致解密无法得到预期结果。下面逐一分析并给出修复方案:
1. 原点判断逻辑错误
你把(0,0)作为无穷远点的替代,但在add()函数里的判断条件写错了:
// 错误:只要x或y为0就误判为原点 if (A[0]==0 || A[1]==0) return B; if (B[0]==0 || B[1]==0) return A;
正确的逻辑是只有当x和y同时为0时,才是原点,否则只是曲线上的普通点。修改为:
if (A[0] == 0 && A[1] == 0) return B; if (B[0] == 0 && B[1] == 0) return A;
2. 标量乘法修改了输入点(严重BUG)
你的sclr_mult()函数在执行过程中直接修改了传入的P数组,这会破坏原有的基点/公钥点数据,导致后续所有依赖这些点的计算全部出错。比如第一次调用sclr_mult(random, Pbase)后,Pbase已经变成了翻倍多次后的点,而非最初的基点。
修复方法是在函数内部创建输入点的临时副本,用副本进行翻倍操作:
int * sclr_mult(int k,int P[2])//using LSB first algorithm { int *temp,i; int *Q = calloc(2,sizeof(int)); Q[0]=0; Q[1]=0; // 创建P的临时副本,避免修改原输入点 int P_copy[2] = {P[0], P[1]}; for(i=31;i>=0;i--) { if((k>>i)&1) break; } for(int j=0;j<=i;j++) { if((k>>j)&1) { temp=add(Q,P_copy); Q[0]=*temp; Q[1]=*(temp+1); } temp=add(P_copy,P_copy); P_copy[0]=*temp; P_copy[1]=*(temp+1); } return Q; }
3. 模运算处理不严谨
计算斜率和点坐标时,需要确保所有运算结果都在模p的范围内,尤其是负数的情况。比如B[1]-A[1]可能为负,应该先调整为正再计算逆元;所有乘法、减法后的结果都要取模并修正符号:
修改add()函数中的关键计算部分:
// 点加法(A≠B)的斜率计算 x = ((B[1] - A[1] + p) % p) * inverse( (B[0] - A[0] + p) % p ); x %= p; // 确保斜率在模p范围内 // 点加倍(A=B)的斜率计算 x = ((3 * (A[0] * A[0] % p) + a) % p) * inverse( (2 * A[1]) % p ); x %= p;
同时,坐标计算后的取模要更严谨:
// 点加法的x坐标 C[0] = (x*x - A[0] - B[0]) % p; // 点加倍的x坐标 C[0] = (x*x - 2*A[0]) % p; // 统一修正负数坐标 if (C[0] < 0) C[0] += p; if (C[1] < 0) C[1] += p;
4. 额外的内存泄漏问题
你的代码中使用calloc分配内存,但没有释放,虽然是概念验证,但养成好习惯很重要。比如在main函数中,每次调用sclr_mult、add后返回的指针,使用完可以用free(temp)释放。
修复后的预期结果
应用这些修改后,运行代码应该能正确解密出原始消息点:
- 基点选
points[5](即(3,10)) - 公钥计算为
11*(3,10),结果为(24, 7) - 解密时会正确抵消掉加密时添加的
kPb项,最终还原出原始消息点(3,10)
其他建议
- 计算椭圆曲线的阶数,验证标量乘法的结果是否在合理范围内,避免使用无效的标量值。
- 把无穷远点的定义封装成常量(比如
#define INF_X 0 #define INF_Y 0),避免硬编码判断条件,提高代码可读性。 - 所有算术运算都先取模,防止整数溢出(即使现在用小
p值,对后续优化也有帮助)。
内容的提问来源于stack exchange,提问作者Shubham Annigeri




