The binascii module provides low-level functions for converting between binary data and ASCII representations. It's the engine behind higher-level modules like base64.

What binascii Does

binascii handles conversions that are common in network protocols and file formats:

  • Binary to hexadecimal (and back)
  • Binary to base64 (and back)
  • CRC checksums

It works at the byte level, giving you precise control over encoding.

hexlify and unhexlify

Convert bytes to hex strings and back:

import binascii
 
# Binary to hex
data = b'Hello'
hex_data = binascii.hexlify(data)
print(hex_data)  # b'48656c6c6f'
 
# Hex to binary
original = binascii.unhexlify(hex_data)
print(original)  # b'Hello'

With a separator (Python 3.8+):

import binascii
 
data = b'\xde\xad\xbe\xef'
hex_sep = binascii.hexlify(data, sep=':')
print(hex_sep)  # b'de:ad:be:ef'
 
# Or use a different separator
hex_space = binascii.hexlify(data, sep=' ', bytes_per_sep=2)
print(hex_space)  # b'dead beef'

b2a_base64 and a2b_base64

Base64 encoding at the byte level:

import binascii
 
# Binary to base64
data = b'Hello, World!'
b64 = binascii.b2a_base64(data)
print(b64)  # b'SGVsbG8sIFdvcmxkIQ==\n'
 
# Note the newline - remove it if needed
b64_clean = binascii.b2a_base64(data, newline=False)
print(b64_clean)  # b'SGVsbG8sIFdvcmxkIQ=='
 
# Base64 to binary
original = binascii.a2b_base64(b64)
print(original)  # b'Hello, World!'

crc32: Checksums

Calculate CRC32 checksums for data integrity:

import binascii
 
data = b'Hello, World!'
checksum = binascii.crc32(data)
print(f"CRC32: {checksum}")  # CRC32: 3964322768
print(f"Hex: {checksum:08x}")  # Hex: ec4ac3d0

Incremental CRC for large data:

import binascii
 
def crc32_file(filepath):
    """Calculate CRC32 of a file in chunks."""
    crc = 0
    with open(filepath, 'rb') as f:
        while chunk := f.read(8192):
            crc = binascii.crc32(chunk, crc)
    return crc & 0xffffffff  # Ensure unsigned
 
# Verify file integrity
crc = crc32_file('data.bin')
print(f"File CRC32: {crc:08x}")

Practical Examples

Debugging Binary Data

import binascii
 
def hexdump(data, width=16):
    """Create a hex dump of binary data."""
    lines = []
    for i in range(0, len(data), width):
        chunk = data[i:i+width]
        hex_part = binascii.hexlify(chunk, sep=' ').decode()
        ascii_part = ''.join(
            chr(b) if 32 <= b < 127 else '.'
            for b in chunk
        )
        lines.append(f"{i:08x}  {hex_part:<{width*3}}  {ascii_part}")
    return '\n'.join(lines)
 
data = b'Hello, World!\x00\x01\x02Binary data'
print(hexdump(data))
# 00000000  48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 00 01 02  Hello, World!...
# 00000010  42 69 6e 61 72 79 20 64 61 74 61                 Binary data

Validating Hex Input

import binascii
 
def parse_hex(hex_string):
    """Parse hex string, handling various formats."""
    # Remove common separators
    cleaned = hex_string.replace(':', '').replace(' ', '').replace('-', '')
    
    try:
        return binascii.unhexlify(cleaned)
    except binascii.Error as e:
        raise ValueError(f"Invalid hex string: {e}")
 
# All these work
print(parse_hex('deadbeef'))          # b'\xde\xad\xbe\xef'
print(parse_hex('de:ad:be:ef'))       # b'\xde\xad\xbe\xef'
print(parse_hex('de ad be ef'))       # b'\xde\xad\xbe\xef'
print(parse_hex('DE-AD-BE-EF'))       # b'\xde\xad\xbe\xef'

Simple Data Integrity Check

import binascii
 
def add_checksum(data):
    """Append CRC32 checksum to data."""
    crc = binascii.crc32(data) & 0xffffffff
    return data + crc.to_bytes(4, 'big')
 
def verify_checksum(data_with_crc):
    """Verify and extract data."""
    data = data_with_crc[:-4]
    stored_crc = int.from_bytes(data_with_crc[-4:], 'big')
    computed_crc = binascii.crc32(data) & 0xffffffff
    
    if stored_crc != computed_crc:
        raise ValueError("Checksum mismatch - data corrupted")
    return data
 
# Usage
original = b'Important data'
with_crc = add_checksum(original)
verified = verify_checksum(with_crc)
print(verified)  # b'Important data'

MAC Address Formatting

import binascii
 
def format_mac(mac_bytes):
    """Format 6-byte MAC address."""
    return binascii.hexlify(mac_bytes, sep=':').decode().upper()
 
def parse_mac(mac_string):
    """Parse MAC address string to bytes."""
    cleaned = mac_string.replace(':', '').replace('-', '')
    return binascii.unhexlify(cleaned)
 
mac = b'\x00\x1a\x2b\x3c\x4d\x5e'
print(format_mac(mac))  # 00:1A:2B:3C:4D:5E
 
parsed = parse_mac('00:1A:2B:3C:4D:5E')
print(parsed)  # b'\x00\x1a+<M^'

binascii vs base64 Module

When to use binascii:

  • You need low-level byte control
  • Working with legacy protocols requiring specific formatting
  • You want the newline parameter for base64
  • Calculating CRC32 checksums

When to use base64:

  • URL-safe encoding (urlsafe_b64encode)
  • Base32, Base16, ASCII85 encoding
  • Higher-level, more readable API
  • Working with strings (automatic encoding handling)
import binascii
import base64
 
data = b'test'
 
# binascii - byte-level, includes newline by default
print(binascii.b2a_base64(data))  # b'dGVzdA==\n'
 
# base64 - cleaner API, no newline
print(base64.b64encode(data))  # b'dGVzdA=='
 
# base64 has URL-safe variant
print(base64.urlsafe_b64encode(b'\xfb\xff'))  # b'--8='
# binascii doesn't

Error Handling

import binascii
 
# Invalid hex length
try:
    binascii.unhexlify('abc')  # Odd length
except binascii.Error as e:
    print(f"Error: {e}")  # Odd-length string
 
# Invalid hex characters
try:
    binascii.unhexlify('xyz')
except binascii.Error as e:
    print(f"Error: {e}")  # Non-hexadecimal digit
 
# Invalid base64
try:
    binascii.a2b_base64('not valid base64!!!')
except binascii.Error as e:
    print(f"Error: {e}")  # Invalid base64

Quick Reference

FunctionPurpose
hexlify(data)Bytes → hex string
unhexlify(hex)Hex string → bytes
b2a_base64(data)Bytes → base64
a2b_base64(b64)Base64 → bytes
crc32(data)Calculate CRC32 checksum

binascii is a workhorse module. It's not glamorous, but when you need precise control over binary-to-text conversions or quick checksums, it's exactly what you need.

React to this post: