"""Base64 encoding in urlsafe format without padding. Fixes Python base64 module's faults: - Padding "==" is optional and not produced by base64url.enc - Base64 type is str (stdlib uses bytes) - Doesn't silently ignore invalid characters and produce garbage """ # We cannot use urlsafe_b64decode which misses the validation argument from base64 import b64decode as _decode from base64 import urlsafe_b64encode as _encode __all__ = ["enc", "dec", "enc_lines", "dec_lines"] def enc(data: bytes) -> str: """Base64 encode bytes to a URL-safe string without padding.""" return _encode(data).decode("ascii").rstrip("=") def dec(s: str, *, validate=True) -> bytes: """Decode standard or urlsafe Base64, with or without padding. Validation raises binascii.Error (ValueError) on invalid characters. If False, any unrecognized characters are silently ignored. """ # Testing whether needs padding is slower, this is the fastest way and constant time. return _decode(s + -len(s) % 4 * "=", altchars=b"-_", validate=validate) def enc_lines(data: bytes, length: int = 76, sep="\n") -> str: """Base64 encode bytes to a URL-safe string split into lines. Returns a string with newlines inserted after every `length` characters. The value should be a multiple of 4 for best compatibility with other systems. """ s = enc(data) return sep.join([s[i : i + length] for i in range(0, len(s), length)]) def dec_lines(s: str) -> bytes: """Decode standard or urlsafe Base64 from a multiline string. All whitespace is ignored to allow formatted input. Raises binascii.Error (ValueError) on other invalid characters. """ return dec("".join(s.split()))