How to compute and verify a digest¶
rustruct.digest() computes a checksum or hash during pack and optionally verifies it
during unpack:
from rustruct import Struct, U8, U32, digest, slice
class Chunk(Struct):
size: U8
data: bytes = slice(len="size")
crc: U32 = digest("crc32", over=("data",))
chunk = Chunk(data=b"abc")
wire = chunk.pack()
assert wire == bytes.fromhex("03 616263 352441c2")
assert Chunk.unpack(wire).crc == 0x352441C2
over=("data",) names a specific sibling span rather than "*" the whole
structure, so the checksum covers only data, not size:
Offset |
Bytes |
Field |
Value |
|---|---|---|---|
0 |
1 |
|
|
1 |
3 |
|
|
4 |
4 |
|
|
Built-in algorithms include CRC presets, IPv4’s Internet checksum, MD5,
SHA-1, and SHA-256. over="*" covers the enclosing scope with the digest’s
own bytes treated as zero. A tuple covers named sibling spans.
A hash algorithm works the same way as a CRC – only the algorithm name and
the resulting field’s width change. A hash digest is a bytes field, not an
integer: its width is whatever the algorithm produces (16 bytes for MD5, 32
for SHA-256), not something declared separately:
import hashlib
class HashedChunk(Struct):
size: U8
data: bytes = slice(len="size")
checksum: bytes = digest("sha256", over=("data",))
hashed = HashedChunk.unpack(HashedChunk(data=b"abc").pack())
assert hashed.checksum == hashlib.sha256(b"abc").digest()
"md5" and "sha1" work the same way, each with their own fixed digest
width.
Verification failures raise rustruct.InvalidDataError(kind=”checksum”).