def point_from_pub_key(pub_key: PubKey, ec: Curve = secp256k1) -> Point: "Return an elliptic curve point tuple from a public key." if isinstance(pub_key, tuple): if ec.is_on_curve(pub_key) and pub_key[1] != 0: return pub_key raise BTClibValueError(f"not a valid public key: {pub_key}") if isinstance(pub_key, BIP32KeyData): return _point_from_xpub(pub_key, ec) try: return _point_from_xpub(pub_key, ec) except (TypeError, BTClibValueError): pass # it must be octets try: return point_from_octets(pub_key, ec) except (TypeError, ValueError) as e: raise BTClibValueError(f"not a public key: {pub_key!r}") from e
def point_from_octets(pub_key: Octets, ec: Curve = secp256k1) -> Point: """Return a tuple (x_Q, y_Q) that belongs to the curve. Return a tuple (x_Q, y_Q) that belongs to the curve according to SEC 1 v.2, section 2.3.4. """ pub_key = bytes_from_octets(pub_key, (ec.p_size + 1, 2 * ec.p_size + 1)) bsize = len(pub_key) # bytes if pub_key[0] in (0x02, 0x03): # compressed point if bsize != ec.p_size + 1: err_msg = "invalid size for compressed point: " err_msg += f"{bsize} instead of {ec.p_size + 1}" raise BTClibValueError(err_msg) x_Q = int.from_bytes(pub_key[1:], byteorder="big") try: y_Q = ec.y_even(x_Q) # also check x_Q validity return x_Q, y_Q if pub_key[0] == 0x02 else ec.p - y_Q except BTClibValueError as e: msg = f"invalid x-coordinate: '{hex_string(x_Q)}'" raise BTClibValueError(msg) from e elif pub_key[0] == 0x04: # uncompressed point if bsize != 2 * ec.p_size + 1: err_msg = "invalid size for uncompressed point: " err_msg += f"{bsize} instead of {2 * ec.p_size + 1}" raise BTClibValueError(err_msg) x_Q = int.from_bytes(pub_key[1:ec.p_size + 1], byteorder="big", signed=False) Q = x_Q, int.from_bytes(pub_key[ec.p_size + 1:], byteorder="big", signed=False) if Q[1] == 0: # infinity point in affine coordinates raise BTClibValueError( "no bytes representation for infinity point") if ec.is_on_curve(Q): return Q raise BTClibValueError(f"point not on curve: {Q}") else: raise BTClibValueError(f"not a point: {pub_key!r}")