How to Implement Checksum Using Python
A checksum is an error detection method widely used in computer networks. It allows the sender to generate a small fixed-size value from a message, which the receiver then uses to verify whether the data arrived intact. If even a single bit changes during transmission, the checksum will detect the discrepancy.
Checksums operate at the higher layers of the network protocol stack (such as TCP, UDP, and IP) and involve two components:
- Checksum Generator (sender side): Computes a checksum from the original message and appends it to the data before sending.
- Checksum Checker (receiver side): Recomputes the checksum from the received data and verifies it against the transmitted checksum.
In this guide, you will learn how the checksum algorithm works step by step and how to implement both the sender and receiver sides in Python.
How the Checksum Algorithm Works
Sender Side: Generating the Checksum
- Divide the message into fixed-size segments of k bits each.
- Add all segments together using binary addition.
- If the sum has more than k bits, wrap around by adding the overflow bits back to the remaining k bits.
- Complement the final sum (flip all bits:
1→0,0→1). This is the checksum. - Send the original message along with the checksum.
Receiver Side: Verifying the Checksum
- Divide the received message into the same k-bit segments.
- Add all segments together with the received checksum.
- Handle any overflow by wrapping around, just like on the sender side.
- Complement the result.
- If the final value is all zeros, the data is accepted (no error detected). Otherwise, an error is detected and the data is rejected.
Checksum-based error detection is not foolproof. It can detect most common transmission errors, but certain error patterns (e.g., two errors that cancel each other out) can go undetected. For stronger guarantees, protocols use CRC (Cyclic Redundancy Check) or cryptographic hashes.
Implementing the Checksum Generator
The sender divides the message into segments, sums them, handles overflow, and complements the result:
def find_checksum(message, k):
"""
Compute the checksum for a binary message string.
Args:
message: A binary string (e.g., '10010101011000111001010011101100')
k: The segment size in bits (e.g., 8)
Returns:
The checksum as a binary string of length k.
"""
# Divide the message into segments of k bits
segments = [message[i:i + k] for i in range(0, len(message), k)]
# Sum all segments as integers
total = sum(int(seg, 2) for seg in segments)
binary_sum = bin(total)[2:] # Remove '0b' prefix
# Wrap around overflow bits until the sum fits in k bits
while len(binary_sum) > k:
overflow = len(binary_sum) - k
binary_sum = bin(int(binary_sum[:overflow], 2) + int(binary_sum[overflow:], 2))[2:]
# Pad with leading zeros if the sum is shorter than k bits
binary_sum = binary_sum.zfill(k)
# Complement the sum (flip every bit)
checksum = ''.join('0' if bit == '1' else '1' for bit in binary_sum)
return checksum
Key details:
bin(total)[2:]converts an integer to its binary string representation and strips the'0b'prefix.- The
whileloop handles cases where the sum overflows beyond k bits by repeatedly folding the overflow back in. .zfill(k)ensures the result is zero-padded to exactly k bits.
Implementing the Checksum Checker
The receiver performs the same segmentation and addition, but also includes the received checksum in the sum:
def verify_checksum(received_message, k, checksum):
"""
Verify the integrity of a received message using its checksum.
Args:
received_message: The binary message string received.
k: The segment size in bits.
checksum: The checksum received from the sender.
Returns:
The complement of the receiver's sum. All zeros means no error.
"""
# Divide the received message into segments of k bits
segments = [received_message[i:i + k] for i in range(0, len(received_message), k)]
# Sum all segments plus the checksum
total = sum(int(seg, 2) for seg in segments) + int(checksum, 2)
binary_sum = bin(total)[2:]
# Wrap around overflow bits
while len(binary_sum) > k:
overflow = len(binary_sum) - k
binary_sum = bin(int(binary_sum[:overflow], 2) + int(binary_sum[overflow:], 2))[2:]
# Pad with leading zeros
binary_sum = binary_sum.zfill(k)
# Complement the result
result = ''.join('0' if bit == '1' else '1' for bit in binary_sum)
return result
If the returned string is all zeros ('00000000' for k=8), the data passed the integrity check.
A frequent error is accidentally including the checksum twice in the receiver's sum. The checksum should be added once; it represents the complement of the sender's segment sum:
# ❌ Incorrect: checksum added twice
total = sum(int(seg, 2) for seg in segments) + int(checksum, 2) + int(checksum, 2)
# ✅ Correct: checksum added once
total = sum(int(seg, 2) for seg in segments) + int(checksum, 2)
Adding it twice produces incorrect verification results and may falsely accept corrupted data.
Complete Implementation with Error Detection
Here is the full program combining both sender and receiver logic, along with the final acceptance/rejection decision:
def find_checksum(message, k):
"""Compute the checksum for a binary message."""
segments = [message[i:i + k] for i in range(0, len(message), k)]
total = sum(int(seg, 2) for seg in segments)
binary_sum = bin(total)[2:]
while len(binary_sum) > k:
overflow = len(binary_sum) - k
binary_sum = bin(int(binary_sum[:overflow], 2) + int(binary_sum[overflow:], 2))[2:]
binary_sum = binary_sum.zfill(k)
checksum = ''.join('0' if bit == '1' else '1' for bit in binary_sum)
return checksum
def verify_checksum(received_message, k, checksum):
"""Verify the received message against the checksum."""
segments = [received_message[i:i + k] for i in range(0, len(received_message), k)]
total = sum(int(seg, 2) for seg in segments) + int(checksum, 2)
binary_sum = bin(total)[2:]
while len(binary_sum) > k:
overflow = len(binary_sum) - k
binary_sum = bin(int(binary_sum[:overflow], 2) + int(binary_sum[overflow:], 2))[2:]
binary_sum = binary_sum.zfill(k)
result = ''.join('0' if bit == '1' else '1' for bit in binary_sum)
return result
if __name__ == "__main__":
sent_message = "10010101011000111001010011101100"
k = 8
# --- Sender Side ---
checksum = find_checksum(sent_message, k)
print(f"Sender Checksum: {checksum}")
# --- Receiver Side (no errors) ---
received_message = "10010101011000111001010011101100"
receiver_result = verify_checksum(received_message, k, checksum)
print(f"Receiver Verification Result: {receiver_result}")
if int(receiver_result, 2) == 0:
print("STATUS: ACCEPTED- No error detected.\n")
else:
print("STATUS: REJECTED- Error detected!\n")
# --- Receiver Side (with error) ---
corrupted_message = "10000101011000111001010011101101" # Bits changed
corrupted_result = verify_checksum(corrupted_message, k, checksum)
print(f"Corrupted Message Verification Result: {corrupted_result}")
if int(corrupted_result, 2) == 0:
print("STATUS: ACCEPTED- No error detected.")
else:
print("STATUS: REJECTED- Error detected!")
Output:
Sender Checksum: 10000101
Receiver Verification Result: 00000000
STATUS: ACCEPTED- No error detected.
Corrupted Message Verification Result: 00001111
STATUS: REJECTED- Error detected!
Step-by-Step Walkthrough
Let's trace through the algorithm with the example message 10010101011000111001010011101100 and segment size k = 8:
Sender side:
| Step | Operation | Result |
|---|---|---|
| 1 | Divide into 8-bit segments | 10010101, 01100011, 10010100, 11101100 |
| 2 | Convert and sum | 149 + 99 + 148 + 236 = 632 |
| 3 | Binary sum | 1001111000 (10 bits- overflows 8) |
| 4 | Wrap overflow: 10 + 01111000 | 01111010 (8 bits) |
| 5 | Complement | 10000101 ← Checksum |
Receiver side (no errors):
| Step | Operation | Result |
|---|---|---|
| 1 | Sum all segments + checksum | 149 + 99 + 148 + 236 + 133 = 765 |
| 2 | Binary sum | 1011111101 (10 bits) |
| 3 | Wrap overflow: 10 + 11111101 | 11111111 |
| 4 | Complement | 00000000 ← All zeros = ACCEPTED |
Making the Implementation More Flexible
The examples above assume the message length is exactly divisible by k and contains exactly 4 segments. Here's a more robust version that handles any message length:
def find_checksum_flexible(message, k):
"""Handle messages of any length, padding the last segment if needed."""
# Pad the message so its length is a multiple of k
remainder = len(message) % k
if remainder != 0:
message = message.zfill(len(message) + (k - remainder))
segments = [message[i:i + k] for i in range(0, len(message), k)]
total = sum(int(seg, 2) for seg in segments)
binary_sum = bin(total)[2:]
while len(binary_sum) > k:
overflow = len(binary_sum) - k
binary_sum = bin(int(binary_sum[:overflow], 2) + int(binary_sum[overflow:], 2))[2:]
binary_sum = binary_sum.zfill(k)
checksum = ''.join('0' if bit == '1' else '1' for bit in binary_sum)
return checksum
When working with real network protocols, the segment size k is typically 16 bits (as used in TCP, UDP, and IP checksums). The algorithm remains identical: only the segment boundaries change.
Complexity Analysis
| Aspect | Value |
|---|---|
| Time Complexity | O(n), where n is the length of the message. Each bit is processed once during segmentation and summation. |
| Space Complexity | O(n/k) for storing the segments, plus O(k) for the checksum string. |
Conclusion
The checksum algorithm provides a straightforward and efficient way to detect transmission errors in computer networks. By dividing a message into fixed-size segments, summing them, and complementing the result, both sender and receiver can verify data integrity with minimal computational overhead.
While checksums cannot detect all possible error patterns, they are fast, easy to implement, and form the foundation of error detection in protocols like TCP, UDP, and IP. For applications requiring stronger error detection, consider using CRC or cryptographic hash functions like SHA-256.