Verify GBLv4 content hashes, memory sections and the manifest signature - #5
Open
yarrrly wants to merge 1 commit into
Open
Verify GBLv4 content hashes, memory sections and the manifest signature#5yarrrly wants to merge 1 commit into
yarrrly wants to merge 1 commit into
Conversation
The manifest already cross-references the rest of a v4 file: CONTENT_HASH covers the payload after it, each UPDATE_MEMORY_SECTION carries the digest of a MEMORY_SECTION_INFO tag and the offset of the section it belongs to, and that info tag carries the digest of the image the section expands to. None of it was checkable, unlike GBL3Image.verify_signature. Adds verify_content_hash, verify_memory_sections, verify_signature, plain_image and the offsets and enums they need, mirroring the v3 API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the API sketched in #4.
What the manifest binds, and where
Checked against public files, since the format page does not give the offsets:
CONTENT_HASHis SHA-256 over everything after theMANIFESTtag.UpdateMemorySection.hashis SHA-256 over the wholeMEMORY_SECTION_INFOtag, its 8 byte header included.UpdateMemorySection.memory_section_positionis a file offset of theMEMORY_SECTIONtag counted from the start of the image, so it includes the root tag's own header.UpdateSe.tlv_positionis not the same kind of pointer: it lands on the SE blob's payload, 8 bytes further in. Both hold on the three Hue files that have an SE blob.MemorySectionInfo.final_image_hash[:32]is SHA-256 of the expanded image andplain_image_sizeis its length. The other 32 bytes are zero in every file I have.hash_type2) is ECDSA P-256 over SHA-256 of the manifest children that follow theSIGNATUREtag: manifest info, bundle version, content hash and update process, headers included. The certificate sits before the signature and is outside the signed range. I found that range by trying; five other plausible ranges fail on the same files. If the series 3 parser in the SDK contradicts this, I would rather match it than keep my reading.API
Plus
GBL4HashType,GBL4SignatureType,GBL4CompressionScheme,GBL4EncryptionScheme,HashValue.digest/matches,GBL4Certificate.public_key,GBL4MemorySection.info/blob, andload_public_keyincrypto.Evidence
Every GBLv4 file I could find in public catalogues, with pygbl at 70c4e59 plus this branch, cryptography 50.0.0, Python 3.14:
Koenkk/zigbee-OTAand their vendor subelementsAll 122 round-trip byte for byte and produce no unknown tags, as before. The Hue images are signed but carry no
CERTIFICATEtag, soverify_signature()raisesKeyErrorunless a key is passed; their sections are AES-CCM, soplain_imagerefuses them andverify_memory_sectionschecks them as far as the info tag digest.Two questions I would rather you decide
verify_signature()with no argument falls back to the key in the image's own certificate. That shows the manifest is internally consistent, not that a vendor you trust signed it, and the docstring says so.GBL3Image.verify_signaturetakes the key instead. Happy to drop the fallback.parse_firmware_image(validate=True)still does nothing for v4, since a failed hash is a different kind of problem from a failed CRC.Decryption is not implemented: the key lives in the device, and I have encrypted images but no key to check a guess against, so I left it out rather than ship an untested counter construction.
One existing test changed
test_manifest_fieldsassertedENCRYPTION_AESCCM in info.features, which holds for the Hue corpus and fails on all 18 IKEA files. It now asserts compression, then cross-checks the feature bit against the per sectionencryption_scheme.Running it
Those 22 tests build their own images and need no fixtures.
tests/test_gbl4.pypicks up the new checks for any.gbl4file intests/files; for the IKEA files the GBL starts at offset 84, after the Matter header, and the Hue ones are subelements of a ZCL OTA file. I did not add a downloading fixture, on the assumption you would rather not have CI reach the network, but I have the URLs and digests if you want them.