Source code for multiaddr.codecs.onion3
import base64
import binascii
from ..codecs import CodecBase
from ..exceptions import BinaryParseError
SIZE = 296
IS_PATH = False
[docs]
class Codec(CodecBase):
SIZE = SIZE
IS_PATH = IS_PATH
[docs]
def to_bytes(self, proto, string):
try:
addr, port = string.split(":", 1)
if addr.endswith(".onion"):
addr = addr[:-6]
if len(addr) != 56:
raise ValueError("Invalid onion3 address length")
if not port.isdigit():
raise ValueError("Invalid onion3 port")
port_num = int(port)
if not 1 <= port_num <= 65535:
raise ValueError("Invalid onion3 port range")
# onion3 address is standard base32 (lowercase, no padding)
try:
addr_bytes = base64.b32decode(addr.upper())
except binascii.Error:
raise ValueError("Invalid base32 encoding")
if len(addr_bytes) != 35:
raise ValueError("Decoded onion3 address must be 35 bytes")
return addr_bytes + port_num.to_bytes(2, byteorder="big")
except (ValueError, UnicodeEncodeError, binascii.Error) as e:
raise BinaryParseError(str(e), string.encode(), proto)
[docs]
def to_string(self, proto, buf):
try:
if len(buf) != 37:
raise ValueError("Invalid onion3 address length")
try:
addr = base64.b32encode(buf[:35]).decode("ascii").lower()
except binascii.Error:
raise ValueError("Invalid base32 encoding")
# Remove padding
addr = addr.rstrip("=")
if len(addr) != 56:
raise ValueError("Invalid onion3 address length")
port = str(int.from_bytes(buf[35:], byteorder="big"))
if not 1 <= int(port) <= 65535:
raise ValueError("Invalid onion3 port range")
return f"{addr}:{port}"
except (ValueError, UnicodeDecodeError, binascii.Error) as e:
raise BinaryParseError(str(e), buf, proto)