Skip to content

Commit a3d50a4

Browse files
gpsheadclaude
andcommitted
Add tests for rejecting non-zero padding bits in base64/base32
RFC 4648 section 3.5 allows decoders to reject encoded data containing non-zero pad bits. Both a2b_base64 (strict_mode=True) and a2b_base32 currently silently discard non-zero trailing bits instead of raising binascii.Error. These tests document the expected behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4561f64 commit a3d50a4

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

Lib/test/test_binascii.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,34 @@ def assertInvalidLength(data, strict_mode=True):
334334
assertInvalidLength(b'A\tB\nC ??DE', # only 5 valid characters
335335
strict_mode=False)
336336

337+
def test_base64_nonzero_padding_bits(self):
338+
# RFC 4648 § 3.5: Implementations MUST reject encoded data if it
339+
# contains pad bits that are not zero.
340+
341+
# 2 data chars + "==": last char has 4 padding bits
342+
# 'A' = 0, 'B' = 1 → 000000 000001 → byte 0x00, leftover 0001 (non-zero)
343+
with self.assertRaises(binascii.Error):
344+
binascii.a2b_base64(self.type2test(b'AB=='), strict_mode=True)
345+
# 'A' = 0, 'P' = 15 → 000000 001111 → byte 0x00, leftover 1111 (non-zero)
346+
with self.assertRaises(binascii.Error):
347+
binascii.a2b_base64(self.type2test(b'AP=='), strict_mode=True)
348+
349+
# 3 data chars + "=": last char has 2 padding bits
350+
# 'A' = 0, 'A' = 0, 'B' = 1 → 000000 000000 000001 → bytes 0x00 0x00,
351+
# leftover 01 (non-zero)
352+
with self.assertRaises(binascii.Error):
353+
binascii.a2b_base64(self.type2test(b'AAB='), strict_mode=True)
354+
# 'A' = 0, 'A' = 0, 'D' = 3 → leftover 11 (non-zero)
355+
with self.assertRaises(binascii.Error):
356+
binascii.a2b_base64(self.type2test(b'AAD='), strict_mode=True)
357+
358+
# Verify that zero padding bits are accepted
359+
binascii.a2b_base64(self.type2test(b'AA=='), strict_mode=True)
360+
binascii.a2b_base64(self.type2test(b'AAA='), strict_mode=True)
361+
362+
# Full quads with no padding have no leftover bits — always valid
363+
binascii.a2b_base64(self.type2test(b'AAAA'), strict_mode=True)
364+
337365
def test_base64_alphabet(self):
338366
alphabet = (b'!"#$%&\'()*+,-012345689@'
339367
b'ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr')
@@ -824,6 +852,48 @@ def assertInvalidLength(*args):
824852
assertInvalidLength(b"BEEFCA=K", b"\t\x08Q\x01")
825853
assertInvalidLength(b"BEEFCA=====K", b"\t\x08Q\x01")
826854

855+
def test_base32_nonzero_padding_bits(self):
856+
# RFC 4648 § 3.5: Implementations MUST reject encoded data if it
857+
# contains pad bits that are not zero.
858+
859+
# 2 data chars + "======": last char has 2 padding bits
860+
# 'AB' → 00000 00001 → byte 0x00, leftover 01 (non-zero)
861+
with self.assertRaises(binascii.Error):
862+
binascii.a2b_base32(self.type2test(b'AB======'))
863+
# 'AD' → 00000 00011 → byte 0x00, leftover 11 (non-zero)
864+
with self.assertRaises(binascii.Error):
865+
binascii.a2b_base32(self.type2test(b'AD======'))
866+
867+
# 4 data chars + "====": last char has 4 padding bits
868+
# 'AAAB' → 00000 00000 00000 00001 → bytes 0x00 0x00, leftover 0001
869+
with self.assertRaises(binascii.Error):
870+
binascii.a2b_base32(self.type2test(b'AAAB===='))
871+
# 'AAAP' → leftover 1111
872+
with self.assertRaises(binascii.Error):
873+
binascii.a2b_base32(self.type2test(b'AAAP===='))
874+
875+
# 5 data chars + "===": last char has 1 padding bit
876+
# 'AAAAB' → 4×00000 + 00001 → bytes 0x00×3, leftover 1 (non-zero)
877+
with self.assertRaises(binascii.Error):
878+
binascii.a2b_base32(self.type2test(b'AAAAB==='))
879+
880+
# 7 data chars + "=": last char has 3 padding bits
881+
# 'AAAAAAB' → 6×00000 + 00001 → bytes 0x00×4, leftover 001
882+
with self.assertRaises(binascii.Error):
883+
binascii.a2b_base32(self.type2test(b'AAAAAAB='))
884+
# 'AAAAAAH' → leftover 111
885+
with self.assertRaises(binascii.Error):
886+
binascii.a2b_base32(self.type2test(b'AAAAAAH='))
887+
888+
# Verify that zero padding bits are accepted
889+
binascii.a2b_base32(self.type2test(b'AA======'))
890+
binascii.a2b_base32(self.type2test(b'AAAA===='))
891+
binascii.a2b_base32(self.type2test(b'AAAAA==='))
892+
binascii.a2b_base32(self.type2test(b'AAAAAAA='))
893+
894+
# Full octet with no padding — always valid
895+
binascii.a2b_base32(self.type2test(b'AAAAAAAA'))
896+
827897
def test_base32_alphabet(self):
828898
alphabet = b'0Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9JjKk'
829899
data = self.type2test(self.rawdata)

0 commit comments

Comments
 (0)