令E为如下椭圆曲线: \[
Y ^2 = X^3 − 15X + 18.
\] 点P = (7, 16) 和 Q = (1, 2)为椭圆曲线上的两点,并构成直线L:
\[
L : Y = \frac{7}{3}X − 1/3.
\] 如下图所示,直线L和椭圆曲线E交于三个点P,Q,R
点R关于x轴对称得到R',我们定义椭圆曲线d上的加法运算, \[
P \oplus Q = R',
\]
和我们通常理解的加法不同,这里的加法是在曲线上进行的,也就是说给出两个点,相加一定可以得到椭圆曲线上的第三个点R',这个点就是加法的结果。
加法运算瓶颈——无穷远点O
还是按照上面的图片,我们尝试做这样的加法 \[
R \oplus R'=?
\]
按照上一节的加法运算,直线RR‘应该和椭圆曲线交于三个点,但是这里没有第三个点,这时候该怎么办?数学家定义了一个无穷远点\(O\),并假设点\(O\)也在椭圆曲线上,这样RR'就能交于椭圆曲线的点\(O\)了,于是有 \[
R \oplus R'=O.
\] 基于无穷远点有这样的性质
无穷远点\(O\)和椭圆曲线上任意点P的连线一定是垂直于x轴的
结合之前的加法运算,于是有公式: \[
P \oplus O = P
\]
这是不是很类似于一个零点,任何点加这个点都是其本身
除此之外,基于无穷远点还有如下性质 \[
P + O = O + P = P \; for \;all\; P ∈ E\\
P + (−P) = O \; for \;all\; P ∈ E\\
(P + Q) + R = P + (Q + R) \; for \;all\;P, Q, R ∈ E\\
P + Q = Q + P \; for \;all\; P, Q ∈ E
\]
四条公式分别代表Albel群(交换群)的4条性质:存在零元,存在逆元,结合律,交换律
加法运算公式
这里给一张图片,很好的讲述了加法的运算公式
减法运算公式
定义负数元的概念:如果椭圆曲线上的一个点P=(a,b),那么点P的负数元为\(\ominus P = (a,-b)\)
T1 = (Z1 * Z1) % p T2 = (y2 * Z1) % p T3 = (x2 * T1) % p T1 = (T1 * T2) % p T2 = (T3 - X1) % p T3 = (T3 + X1) % p T4 = (T2 * T2) % p T1 = (T1 - Y1) % p Z3 = (Z1 * T2) % p T2 = (T2 * T4) % p T3 = (T3 * T4) % p T5 = (T1 * T1) % p T4 = (X1 * T4) % p X3 = (T5 - T3) % p T2 = (Y1 * T2) % p T3 = (T4 - X3) % p T1 = (T1 * T3) % p Y3 = (T1 - T2) % p
form = '%%0%dx' % self.para_len
return (form % X3, form % Y3, form % Z3)
defkp(self, k_int, Point_xy): # kP运算,即k倍点的运算函数 Point = Point_xy k = k_int mask_str = '8' for i inrange(self.para_len - 1): mask_str += '0' mask = int(mask_str, 16) Temp = Point flag = False for n inrange(self.para_len * 4): #每个16进制4个bit if (flag): Temp = self.double_point(Temp) if (k & mask) != 0: #用与操作判断k的最高位是否为0 if (flag): Temp = self.add_point(Temp, Point) else: flag = True Temp = Point k = k << 1 return self._convert_jacb_to_nor(Temp)
def_convert_jacb_to_nor(self, Point): # Jacobian加重射影坐标转换成仿射坐标 len_2 = 2 * self.para_len x = int(Point[0], 16) y = int(Point[1], 16) z = int(Point[2], 16) p = int(self.ecc_table['p'], base=16) z_inv = pow(z, p - 2, p) z_invSquar = (z_inv * z_inv) % p z_invQube = (z_invSquar * z_inv) % p x_new = (x * z_invSquar) % p y_new = (y * z_invQube) % p z_new = (z * z_inv) % p if z_new == 1: form = '%%0%dx' % self.para_len
return (form % x_new, form % y_new, form % z_new) else: returnNone
defencrypt(self, data_str): # 加密函数,data消息字符串 data = data_str.encode() #将消息转化为bytes流 msg = data.hex() # 消息转化为16进制字符串 G = (self.ecc_table['g_x'],self.ecc_table['g_y'],"1") k = 2#int(func.random_hex(self.para_len),16) #1.产生随机数k \in [1,n-1] C1 = self.kp(k,G) #2.计算[k]G = (x1,y1) C1 = self._convert_jacb_to_nor(C1) C1 = C1[0]+C1[1] xy = self.kp(k,self.public_key) #3.计算点s = [k]pk x2 = xy[0] y2 = xy[1] m_len = len(msg) t = sm3.sm3_kdf((x2+y2).encode('utf8'), m_len/2)#5.计算t = KDF(x2||y2,klen) ifint(t,16)==0: returnNone else: form = '%%0%dx' % m_len #两个百分号代表% C2 = form % (int(msg, 16) ^ int(t, 16)) #6.计算C2 = M 异或 t,C2的长度理应为消息M的长度 C3 = sm3.sm3_hash([ i for i inbytes.fromhex('%s%s%s'% (x2,msg,y2))#7.计算哈希函数C3 = Hash(x2 || M || y2) ]) returnbytes.fromhex('%s%s%s' % (C1,C3,C2))
defdecrypt(self, data): # 解密函数,data密文(bytes) data = data.hex() len_2 = 2 * self.para_len len_3 = len_2 + 64 C1 = self.Str_coordinate_to_jacobian(data[0:len_2]) #1.提取出C1,并转化为坐标点 C3 = data[len_2:len_3] C2 = data[len_3:] xyz = self.kp(int(self.private_key,16),C1)#3.计算[sk]C1 = (x2,y2) xy = self._convert_jacb_to_nor(xyz) # print('xy = %s' % xy) x2 = xy[0] y2 = xy[1] cl = len(C2) t = sm3.sm3_kdf((x2+y2).encode('utf8'), cl/2)#4.计算t = KDF(x2||y2,klen) ifint(t, 16) == 0: returnNone else: form = '%%0%dx' % cl M = form % (int(C2,16) ^ int(t,16))#5.恢复明文M = C2 异或 t u = sm3.sm3_hash([ i for i inbytes.fromhex('%s%s%s'% (x2,M,y2)) ]) returnbytes.fromhex(M)
defStr_coordinate_to_jacobian(self,Point_str): Point = Point_str x = Point[0:self.para_len] y = Point[self.para_len:2*self.para_len] z = "1" return (x,y,z)
if __name__ == "__main__": # sm2的公私钥 SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5' SM2_PUBLIC_KEY = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207' operator = CryptSM2(SM2_PRIVATE_KEY,SM2_PUBLIC_KEY) c = operator.encrypt("网络空间安全") print(base64.b64encode(c)) result = operator.decrypt(c).decode(encoding="utf-8") print(result)
# n = int(default_ecc_table['n'],16) # G = operator.Str_coordinate_to_jacobian(SM2_PRIVATE_KEY) # print(operator.kp(n,G))