题目让伪造出一组可以验签通过的摘要和签名,方法为绕过公钥。
1、分析verify代码,可以发现当r+s=n时,公钥不发挥作用,而r,s是签名值,使s=1,r=n-1,构造sign = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D541220000000000000000000000000000000000000000000000000000000000000001"
2、通过判定条件r == ((e + x) % int(default_table['n'], base=16)),得到e=(r-x)%n
在verify中使用print('%064x' % ((r - x) % int(default_table['n'], base=16)))得到e
原代码中%064x表示格式化为 64 位宽的十六进制(小写),前面补零


default_table = {
    'n': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
    'p': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
    'g': '32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7'
         'bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0',
    'a': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
    'b': '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
}

class Crypt():

    def __init__(self, private_key, public_key, mode=0):
        self.private_key = private_key
        if public_key.startswith("04"):
            self.public_key = public_key[2:]
        else:
            self.public_key = public_key
        self.para_len = len(default_table['n'])
        self.ecc_a3 = (
            int(default_table['a'], base=16) + 3) % int(default_table['p'], base=16)
        assert mode in (0, 1), 'mode must be one of (0, 1)'
        self.mode = mode

    def _kg(self, k, Point):  # kP运算
        if k == 0:
            return None#无穷远点
        Point = '%s%s' % (Point, '1')
        mask_str = '8'
        for i in range(self.para_len - 1):
            mask_str += '0'
        mask = int(mask_str, 16)
        Temp = Point
        flag = False
        for n in range(self.para_len * 4):
            if (flag):
                Temp = self._double_point(Temp)
            if (k & mask) != 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 _double_point(self, Point):  # 倍点
        if Point is None:
            return None
        l = len(Point)
        len_2 = 2 * self.para_len
        if l < self.para_len * 2:
            return None
        else:
            x1 = int(Point[0:self.para_len], 16)
            y1 = int(Point[self.para_len:len_2], 16)
            if l == len_2:
                z1 = 1
            else:
                z1 = int(Point[len_2:], 16)

            T6 = (z1 * z1) % int(default_table['p'], base=16)
            T2 = (y1 * y1) % int(default_table['p'], base=16)
            T3 = (x1 + T6) % int(default_table['p'], base=16)
            T4 = (x1 - T6) % int(default_table['p'], base=16)
            T1 = (T3 * T4) % int(default_table['p'], base=16)
            T3 = (y1 * z1) % int(default_table['p'], base=16)
            T4 = (T2 * 8) % int(default_table['p'], base=16)
            T5 = (x1 * T4) % int(default_table['p'], base=16)
            T1 = (T1 * 3) % int(default_table['p'], base=16)
            T6 = (T6 * T6) % int(default_table['p'], base=16)
            T6 = (self.ecc_a3 * T6) % int(default_table['p'], base=16)
            T1 = (T1 + T6) % int(default_table['p'], base=16)
            z3 = (T3 + T3) % int(default_table['p'], base=16)
            T3 = (T1 * T1) % int(default_table['p'], base=16)
            T2 = (T2 * T4) % int(default_table['p'], base=16)
            x3 = (T3 - T5) % int(default_table['p'], base=16)

            if (T5 % 2) == 1:
                T4 = (T5 + ((T5 + int(default_table['p'], base=16)) >> 1) - T3) % int(
                    default_table['p'], base=16)
            else:
                T4 = (T5 + (T5 >> 1) - T3) % int(default_table['p'], base=16)

            T1 = (T1 * T4) % int(default_table['p'], base=16)
            y3 = (T1 - T2) % int(default_table['p'], base=16)

            form = '%%0%dx' % self.para_len
            form = form * 3
            return form % (x3, y3, z3)

    def _add_point(self, P1, P2):  # 点加函数,P2点为仿射坐标即z=1,P1为Jacobian加重射影坐标
        if P1 is None:
            return P2
        if P2 is None:
            return P1
        len_2 = 2 * self.para_len
        l1 = len(P1)
        l2 = len(P2)
        if (l1 < len_2) or (l2 < len_2):
            return None
        else:
            X1 = int(P1[0:self.para_len], 16)
            Y1 = int(P1[self.para_len:len_2], 16)
            if (l1 == len_2):
                Z1 = 1
            else:
                Z1 = int(P1[len_2:], 16)
            x2 = int(P2[0:self.para_len], 16)
            y2 = int(P2[self.para_len:len_2], 16)

            T1 = (Z1 * Z1) % int(default_table['p'], base=16)
            T2 = (y2 * Z1) % int(default_table['p'], base=16)
            T3 = (x2 * T1) % int(default_table['p'], base=16)
            T1 = (T1 * T2) % int(default_table['p'], base=16)
            T2 = (T3 - X1) % int(default_table['p'], base=16)
            T3 = (T3 + X1) % int(default_table['p'], base=16)
            T4 = (T2 * T2) % int(default_table['p'], base=16)
            T1 = (T1 - Y1) % int(default_table['p'], base=16)
            Z3 = (Z1 * T2) % int(default_table['p'], base=16)
            T2 = (T2 * T4) % int(default_table['p'], base=16)
            T3 = (T3 * T4) % int(default_table['p'], base=16)
            T5 = (T1 * T1) % int(default_table['p'], base=16)
            T4 = (X1 * T4) % int(default_table['p'], base=16)
            X3 = (T5 - T3) % int(default_table['p'], base=16)
            T2 = (Y1 * T2) % int(default_table['p'], base=16)
            T3 = (T4 - X3) % int(default_table['p'], base=16)
            T1 = (T1 * T3) % int(default_table['p'], base=16)
            Y3 = (T1 - T2) % int(default_table['p'], base=16)

            form = '%%0%dx' % self.para_len
            form = form * 3
            return form % (X3, Y3, Z3)

    def _convert_jacb_to_nor(self, Point):  # Jacobian加重射影坐标转换成仿射坐标
        if Point is None:
            return None
        len_2 = 2 * self.para_len
        x = int(Point[0:self.para_len], 16)
        y = int(Point[self.para_len:len_2], 16)
        z = int(Point[len_2:], 16)
        z_inv = pow(
            z, int(default_table['p'], base=16) - 2, int(default_table['p'], base=16))
        z_invSquar = (z_inv * z_inv) % int(default_table['p'], base=16)
        z_invQube = (z_invSquar * z_inv) % int(default_table['p'], base=16)
        x_new = (x * z_invSquar) % int(default_table['p'], base=16)
        y_new = (y * z_invQube) % int(default_table['p'], base=16)
        z_new = (z * z_inv) % int(default_table['p'], base=16)
        if z_new == 1:
            form = '%%0%dx' % self.para_len
            form = form * 2
            return form % (x_new, y_new)
        else:
            return None

    def verify(self, Sign, data):
        if Sign is None or Sign == '':
            return None
        r = int(Sign[0:self.para_len], 16)
        s = int(Sign[self.para_len:2*self.para_len], 16)
        e = int(data.hex(), 16)
        # 参数合法性
        if not (1 <= r < int(default_table['n'], base=16) and 1 <= s < int(default_table['n'], base=16)):
            return False
        t = r + s
        if t == 0:
            return False
        else:
            t = t % int(default_table['n'], base=16)

        # if self.public_key is None or self.public_key == '':
        #     return None

        P1 = self._kg(s, default_table['g'])
        # print(P1)
        if P1 is None:
            return False    
        P2 = self._kg(t, self.public_key)
        if P1 == P2:
            P1 = '%s%s' % (P1, 1)
            P1 = self._double_point(P1)
        else:
            P1 = '%s%s' % (P1, 1)
            P1 = self._add_point(P1, P2)
            P1 = self._convert_jacb_to_nor(P1)

        x = int(P1[0:self.para_len], 16)
        # print(x)
        print('%064x' % ((r - x) % int(default_table['n'], base=16)))
        return r == ((e + x) % int(default_table['n'], base=16))

    def sign(self, data):
        k = 1
        k = secrets.randbelow(int(default_table['n'], 16) - 1) + 1
        if not (1 <= k <= int(default_table['n'], base=16)- 1):
            return None
        E = data.hex()  
        e = int(E, 16)
        if self.private_key is None or self.private_key == '':
            return None

        d = int(self.private_key, 16)
        P1 = self._kg(k, default_table['g'])

        x = int(P1[0:self.para_len], 16)
        A = ((e + x) % int(default_table['n'], base=16))
        if A == 0 or A + k == int(default_table['n'], base=16):
            return None
        d_1 = pow(
            d+1, int(default_table['n'], base=16) - 2, int(default_table['n'], base=16))
        B = (d_1*(k + A) - A) % int(default_table['n'], base=16)
        # print('%064x' % ((A-x)%int(default_table['n'], base=16)))
        if B == 0:
            return None
        else:
            return '%064x%064x' % (A, B)


if __name__ == '__main__':
    crypt = Crypt(
        # 公钥格式为64字节的16进制字符串
        public_key='',
        # 私钥格式为32字节的16进制字符串
        private_key=''
    )
    data = 'cd3b51d2e0e67ee6a066fbb995c6366ae220d3ab2f5ff949e261ae800688cc5b'
    # sign = crypt.sign(bytes.fromhex(data))
    sign = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D541220000000000000000000000000000000000000000000000000000000000000001"
    verify = crypt.verify(sign,bytes.fromhex(data))
    print(verify)