def recoverECDSAKey(token, hashalg, curve, compressed): header, body, sig = token.split(".") rs = base64.b64decode(sig + "==") n = len(rs) // 2 r = int(rs[:n].hex(), 16) s = int(rs[n:].hex(), 16) # compute the 2 points having r has X coordinate y1, y2 = mod_sqrt(r**3 + curve.a * r + curve.b, curve.p) R1 = Point(r, y1, curve) R2 = Point(r, y2, curve) # compute r^1 r_inv = int(gmpy2.invert(r, curve.q)) # compute message hash message = header + "." + body h = int(hashalg.new(message.encode()).hexdigest(), 16) G = Point(curve.gx, curve.gy, curve) # recover the two possible public keys k1 = r_inv * (s * R1 - h * G) k2 = r_inv * (s * R2 - h * G) k1_ = ECC.construct(curve=curve.name.lower(), point_x=k1.x, point_y=k1.y) k2_ = ECC.construct(curve=curve.name.lower(), point_x=k2.x, point_y=k2.y) print("Found 2 public ECDSA keys !") print(f"x={k1.x}\ny={k1.y}") print(k1_.export_key(format="PEM", compress=compressed)) print("") print(f"x={k2.x}\ny={k2.y}") print(k2_.export_key(format="PEM", compress=compressed))
def bytes_to_point(b: bytes) -> Point: """Takes a compressed bytes representation and returns the corresponding point""" if b == 0: return Point.IDENTITY_ELEMENT p = CURVE.p yp, x_enc = b[0], b[1:] yp = 0 if yp == 2 else 1 x = int.from_bytes(x_enc, "big") y = mod_sqrt((x ** 3 + CURVE.a * x + CURVE.b) % p, p)[0] if y % 2 == yp: return Point(x, y, CURVE) else: return Point(x, p - y, CURVE)
def elliptic_hash(msg: bytes, CURVE: Curve): p = CURVE.p i = 0 while True: i += 1 prefixed_msg = str(i).encode() + msg h = sha256(prefixed_msg).hexdigest() x = int(h, 16) if x >= p: continue y_sq = (x ** 3 + CURVE.a * x + CURVE.b) % p y = mod_sqrt(y_sq, p)[0] if CURVE.is_point_on_curve((x, y)): b = int(md5(prefixed_msg).hexdigest(), 16) % 2 return Point(x, y, CURVE) if b else Point(x, p - y, CURVE)
def from_extended_key(cls, key, is_public=False): """ Create a BIP32Key by importing from extended private or public key string If public is True, return a public-only key regardless of input type. """ # Sanity checks if isinstance(key, str): raw = check_decode(key) else: raw = b'\x00\x00\x00\x00' + key if len(raw) != 78: raise ValueError("extended key format wrong length") # Verify address version/type version = raw[:4] if version == b'\x00\x00\x00\x00': is_testnet = None is_pubkey = None elif version in EX_MAIN_PRIVATE: is_testnet = False is_pubkey = False elif version in EX_TEST_PRIVATE: is_testnet = True is_pubkey = False elif version in EX_MAIN_PUBLIC: is_testnet = False is_pubkey = True elif version in EX_TEST_PUBLIC: is_testnet = True is_pubkey = True else: raise ValueError("unknown extended key version") # Extract remaining fields depth = raw[4] fpr = raw[5:9] child = struct.unpack(">L", raw[9:13])[0] chain = raw[13:45] data = raw[45:78] # check prefix of key is_pubkey = (data[0] == 2 or data[0] == 3) # Extract private key or public key point if not is_pubkey: secret = int.from_bytes(data[1:], 'big') public = get_public_key(secret, secp256k1) else: # Recover public curve point from compressed key # Python3 FIX lsb = data[0] & 1 if type(data[0]) == int else ord(data[0]) & 1 x = int.from_bytes(data[1:], 'big') ys = (x**3 + 7) % FIELD_ORDER # y^2 = x^3 + 7 mod p y, _ = mod_sqrt(ys, FIELD_ORDER) if y & 1 != lsb: y = FIELD_ORDER - y secret = None public = Point(x, y, secp256k1) if not is_pubkey and is_public: return cls(secret=None, public=public, chain=chain, depth=depth, index=child, fpr=fpr, path='m') else: return cls(secret=secret, public=public, chain=chain, depth=depth, index=child, fpr=fpr, path='m')