check the config's CRC32 checksum while reading

This commit is contained in:
Brian S. Stephan 2023-06-28 14:34:28 -05:00
parent 51445163a8
commit 39fa558741
Signed by: bss
GPG Key ID: 3DE06D3180895FCB
2 changed files with 20 additions and 2 deletions

View File

@ -1,5 +1,6 @@
"""Interact with the protobuf config from a picotool flash dump of a GP2040-CE board.""" """Interact with the protobuf config from a picotool flash dump of a GP2040-CE board."""
import argparse import argparse
import binascii
import logging import logging
from google.protobuf.json_format import MessageToJson from google.protobuf.json_format import MessageToJson
@ -21,6 +22,10 @@ FOOTER_MAGIC = b'\x65\xe3\xf1\xd2'
################# #################
class ConfigCrcError(ValueError):
"""Exception raised when the CRC checksum in the footer doesn't match the actual content's."""
class ConfigLengthError(ValueError): class ConfigLengthError(ValueError):
"""Exception raised when a length sanity check fails.""" """Exception raised when a length sanity check fails."""
@ -52,7 +57,7 @@ def get_config_footer(content: bytes) -> tuple[int, int, str]:
Args: Args:
content: bytes from a GP2040-CE board's storage section content: bytes from a GP2040-CE board's storage section
Returns: Returns:
the discovered config size, config CRC, and magic from the config footer the discovered config size, config CRC checksum, and magic from the config footer
Raises: Raises:
ConfigLengthError, ConfigMagicError: if the provided bytes are not a config footer ConfigLengthError, ConfigMagicError: if the provided bytes are not a config footer
""" """
@ -70,10 +75,15 @@ def get_config_footer(content: bytes) -> tuple[int, int, str]:
config_crc = int.from_bytes(reversed(footer[4:8]), 'big') config_crc = int.from_bytes(reversed(footer[4:8]), 'big')
config_magic = f'0x{footer[8:12].hex()}' config_magic = f'0x{footer[8:12].hex()}'
# one last sanity check # more sanity checks
if len(content) < config_size + FOOTER_SIZE: if len(content) < config_size + FOOTER_SIZE:
raise ConfigLengthError("provided content is not large enough according to the config footer!") raise ConfigLengthError("provided content is not large enough according to the config footer!")
content_crc = binascii.crc32(content[-(config_size + 12):-12])
if config_crc != content_crc:
raise ConfigCrcError(f"provided content CRC checksum {content_crc} does not match footer's expected CRC "
f"checksum {config_crc}!")
logger.debug("detected footer (size:%s, crc:%s, magic:%s", config_size, config_crc, config_magic) logger.debug("detected footer (size:%s, crc:%s, magic:%s", config_size, config_crc, config_magic)
return config_size, config_crc, config_magic return config_size, config_crc, config_magic

View File

@ -56,6 +56,14 @@ def test_config_footer_bad_magic(storage_dump):
_, _, _ = storage.get_config_footer(unmagical) _, _, _ = storage.get_config_footer(unmagical)
def test_config_footer_bad_crc(storage_dump):
"""Test that a config footer isn't detected if the CRC checksums don't match."""
corrupt = bytearray(storage_dump)
corrupt[-50:-40] = bytearray(0*10)
with pytest.raises(storage.ConfigCrcError):
_, _, _ = storage.get_config_footer(corrupt)
def test_config_fails_without_pb2s(storage_dump): def test_config_fails_without_pb2s(storage_dump):
"""Test that we need the config_pb2 to exist/be compiled for reading the config to work.""" """Test that we need the config_pb2 to exist/be compiled for reading the config to work."""
with pytest.raises(ModuleNotFoundError): with pytest.raises(ModuleNotFoundError):