Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions Doc/library/base64.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ POST request.


.. function:: b64decode(s, altchars=None, validate=False)
b64decode(s, altchars=None, validate=True, *, ignorechars)

Decode the Base64 encoded :term:`bytes-like object` or ASCII string
*s* and return the decoded :class:`bytes`.
Expand All @@ -84,16 +85,24 @@ POST request.
A :exc:`binascii.Error` exception is raised
if *s* is incorrectly padded.

If *validate* is ``False`` (the default), characters that are neither
If *ignorechars* is specified, it should be a byte string containing
characters to ignore from the input, and *validate* is ``True`` by default.
Otherwise *validate* is ``False`` by default.

If *validate* is false, characters that are neither
in the normal base-64 alphabet nor the alternative alphabet are
discarded prior to the padding check. If *validate* is ``True``,
these non-alphabet characters in the input result in a
:exc:`binascii.Error`.
discarded prior to the padding check.
If *validate* is true, these non-alphabet characters in the input
result in a :exc:`binascii.Error`.

For more information about the strict base64 check, see :func:`binascii.a2b_base64`

May assert or raise a :exc:`ValueError` if the length of *altchars* is not 2.

.. versionchanged:: next
Added the *ignorechars* parameter.


.. function:: standard_b64encode(s)

Encode :term:`bytes-like object` *s* using the standard Base64 alphabet
Expand Down
9 changes: 9 additions & 0 deletions Doc/library/binascii.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ The :mod:`binascii` module defines the following functions:


.. function:: a2b_base64(string, /, *, strict_mode=False)
a2b_base64(string, /, *, strict_mode=True, ignorechars)
Convert a block of base64 data back to binary and return the binary data. More
than one line may be passed at a time.

If *ignorechars* is specified, it should be a byte string containing
characters to ignore from the input when *strict_mode* is true.
*strict_mode* is ``True`` by default, if *ignorechars* is specified,
``False`` otherwise.

If *strict_mode* is true, only valid base64 data will be converted. Invalid base64
data will raise :exc:`binascii.Error`.

Expand All @@ -66,6 +72,9 @@ The :mod:`binascii` module defines the following functions:
.. versionchanged:: 3.11
Added the *strict_mode* parameter.

.. versionchanged:: next
Added the *ignorechars* parameter.


.. function:: b2a_base64(data, *, wrapcol=0, newline=True)

Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,18 @@ base64
* Added the *wrapcol* parameter in :func:`~base64.b64encode`.
(Contributed by Serhiy Storchaka in :gh:`143214`.)

* Added the *ignorechars* parameter in :func:`~base64.b64decode`.
(Contributed by Serhiy Storchaka in :gh:`144001`.)

binascii
--------

* Added the *wrapcol* parameter in :func:`~binascii.b2a_base64`.
(Contributed by Serhiy Storchaka in :gh:`143214`.)

* Added the *ignorechars* parameter in :func:`~binascii.a2b_base64`.
(Contributed by Serhiy Storchaka in :gh:`144001`.)


calendar
--------
Expand Down
23 changes: 17 additions & 6 deletions Lib/base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
]


_NOT_SPECIFIED = ['NOT SPECIFIED']

bytes_types = (bytes, bytearray) # Types acceptable as binary data

def _bytes_from_decode_data(s):
Expand Down Expand Up @@ -62,7 +64,7 @@ def b64encode(s, altchars=None, *, wrapcol=0):
return encoded


def b64decode(s, altchars=None, validate=False):
def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, *, ignorechars=_NOT_SPECIFIED):
"""Decode the Base64 encoded bytes-like object or ASCII string s.

Optional altchars must be a bytes-like object or ASCII string of length 2
Expand All @@ -72,10 +74,14 @@ def b64decode(s, altchars=None, validate=False):
The result is returned as a bytes object. A binascii.Error is raised if
s is incorrectly padded.

If validate is False (the default), characters that are neither in the
normal base-64 alphabet nor the alternative alphabet are discarded prior
to the padding check. If validate is True, these non-alphabet characters
in the input result in a binascii.Error.
If ignorechars is specified, it should be a byte string containing
characters to ignore from the input, and validate is True by default.
Otherwise validate is False by default.

If validate is false, characters that are neither in the normal base-64
alphabet nor the alternative alphabet are discarded prior to the
padding check. If validate is true, these non-alphabet characters in
the input result in a binascii.Error if they are not in ignorechars.
For more information about the strict base64 check, see:

https://docs.python.org/3.11/library/binascii.html#binascii.a2b_base64
Expand All @@ -85,7 +91,12 @@ def b64decode(s, altchars=None, validate=False):
altchars = _bytes_from_decode_data(altchars)
assert len(altchars) == 2, repr(altchars)
s = s.translate(bytes.maketrans(altchars, b'+/'))
return binascii.a2b_base64(s, strict_mode=validate)
if validate is _NOT_SPECIFIED:
validate = ignorechars is not _NOT_SPECIFIED
if ignorechars is _NOT_SPECIFIED:
ignorechars = b''
return binascii.a2b_base64(s, strict_mode=validate,
ignorechars=ignorechars)


def standard_b64encode(s):
Expand Down
37 changes: 26 additions & 11 deletions Lib/test/test_base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,22 +298,22 @@ def test_b64decode_padding_error(self):

def test_b64decode_invalid_chars(self):
# issue 1466065: Test some invalid characters.
tests = ((b'%3d==', b'\xdd'),
(b'$3d==', b'\xdd'),
(b'[==', b''),
(b'YW]3=', b'am'),
(b'3{d==', b'\xdd'),
(b'3d}==', b'\xdd'),
(b'@@', b''),
(b'!', b''),
(b"YWJj\n", b"abc"),
(b'YWJj\nYWI=', b'abcab'))
tests = ((b'%3d==', b'\xdd', b'%$'),
(b'$3d==', b'\xdd', b'%$'),
(b'[==', b'', None),
(b'YW]3=', b'am', b']'),
(b'3{d==', b'\xdd', b'{}'),
(b'3d}==', b'\xdd', b'{}'),
(b'@@', b'', b'@!'),
(b'!', b'', b'@!'),
(b"YWJj\n", b"abc", b'\n'),
(b'YWJj\nYWI=', b'abcab', b'\n'))
funcs = (
base64.b64decode,
base64.standard_b64decode,
base64.urlsafe_b64decode,
)
for bstr, res in tests:
for bstr, res, ignorechars in tests:
for func in funcs:
with self.subTest(bstr=bstr, func=func):
self.assertEqual(func(bstr), res)
Expand All @@ -322,6 +322,21 @@ def test_b64decode_invalid_chars(self):
base64.b64decode(bstr, validate=True)
with self.assertRaises(binascii.Error):
base64.b64decode(bstr.decode('ascii'), validate=True)
with self.assertRaises(binascii.Error):
base64.b64decode(bstr, ignorechars=b'')
if ignorechars is not None:
self.assertEqual(
base64.b64decode(bstr, ignorechars=ignorechars),
res)

with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars=bytearray())
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars='')
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars=[])
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars=None)

# Normal alphabet characters not discarded when alternative given
res = b'\xfb\xef\xff'
Expand Down
51 changes: 42 additions & 9 deletions Lib/test/test_binascii.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,16 @@ def assertExcessPadding(data, non_strict_mode_expected_result: bytes):

# Test excess data exceptions
assertExcessData(b'ab==a', b'i')
assertExcessData(b'ab===', b'i')
assertExcessData(b'ab====', b'i')
assertExcessData(b'ab==:', b'i')
assertExcessPadding(b'ab===', b'i')
assertExcessPadding(b'ab====', b'i')
assertNonBase64Data(b'ab==:', b'i')
assertExcessData(b'abc=a', b'i\xb7')
assertExcessData(b'abc=:', b'i\xb7')
assertExcessData(b'ab==\n', b'i')
assertExcessData(b'abc==', b'i\xb7')
assertExcessData(b'abc===', b'i\xb7')
assertExcessData(b'abc====', b'i\xb7')
assertExcessData(b'abc=====', b'i\xb7')
assertNonBase64Data(b'abc=:', b'i\xb7')
assertNonBase64Data(b'ab==\n', b'i')
assertExcessPadding(b'abc==', b'i\xb7')
assertExcessPadding(b'abc===', b'i\xb7')
assertExcessPadding(b'abc====', b'i\xb7')
assertExcessPadding(b'abc=====', b'i\xb7')

# Test non-base64 data exceptions
assertNonBase64Data(b'\nab==', b'i')
Expand All @@ -170,12 +170,45 @@ def assertExcessPadding(data, non_strict_mode_expected_result: bytes):
assertLeadingPadding(b'=====', b'')
assertDiscontinuousPadding(b'ab=c=', b'i\xb7')
assertDiscontinuousPadding(b'ab=ab==', b'i\xb6\x9b')
assertNonBase64Data(b'ab=:=', b'i')
assertExcessPadding(b'abcd=', b'i\xb7\x1d')
assertExcessPadding(b'abcd==', b'i\xb7\x1d')
assertExcessPadding(b'abcd===', b'i\xb7\x1d')
assertExcessPadding(b'abcd====', b'i\xb7\x1d')
assertExcessPadding(b'abcd=====', b'i\xb7\x1d')

def test_base64_invalidchars(self):
def assertNonBase64Data(data, expected, ignorechars):
data = self.type2test(data)
assert_regex = r'(?i)Only base64 data'
self.assertEqual(binascii.a2b_base64(data), expected)
with self.assertRaisesRegex(binascii.Error, assert_regex):
binascii.a2b_base64(data, strict_mode=True)
with self.assertRaisesRegex(binascii.Error, assert_regex):
binascii.a2b_base64(data, ignorechars=b'')
self.assertEqual(binascii.a2b_base64(data, ignorechars=ignorechars),
expected)
self.assertEqual(binascii.a2b_base64(data, strict_mode=False, ignorechars=b''),
expected)

assertNonBase64Data(b'\nab==', b'i', ignorechars=b'\n')
assertNonBase64Data(b'ab:(){:|:&};:==', b'i', ignorechars=b':;(){}|&')
assertNonBase64Data(b'a\nb==', b'i', ignorechars=b'\n')
assertNonBase64Data(b'a\x00b==', b'i', ignorechars=b'\x00')
assertNonBase64Data(b'ab==:', b'i', ignorechars=b':')
assertNonBase64Data(b'abc=:', b'i\xb7', ignorechars=b':')
assertNonBase64Data(b'ab==\n', b'i', ignorechars=b'\n')
assertNonBase64Data(b'ab=:=', b'i', ignorechars=b':')

data = self.type2test(b'a\nb==')
with self.assertRaises(TypeError):
binascii.a2b_base64(data, ignorechars=bytearray())
with self.assertRaises(TypeError):
binascii.a2b_base64(data, ignorechars='')
with self.assertRaises(TypeError):
binascii.a2b_base64(data, ignorechars=[])
with self.assertRaises(TypeError):
binascii.a2b_base64(data, ignorechars=None)

def test_base64errors(self):
# Test base64 with invalid padding
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added the *ignorechars* parameter in :func:`binascii.a2b_base64` and
:func:`base64.b64decode`.
Loading
Loading