How Chromium Packages and Signs CRX3 Extension Archives

This article provides a quick overview of how the Chromium web browser packages and cryptographically signs CRX3 extension archives to verify file integrity and authenticity during installation. It covers the binary layout of the CRX3 file format, the generation of Protocol Buffer headers, the cryptographic signing process, and the step-by-step verification routine the browser executes before loading an extension.

The CRX3 Binary Structure

A CRX3 file is a standard ZIP archive prepended with a specialized binary header. The file is structured sequentially in five distinct parts:

  1. Magic Number (4 bytes): Always set to the ASCII characters Cr24, which identifies the file to the browser as a Chromium extension.
  2. Version (4 bytes): A little-endian integer set to 3, indicating the CRX3 format.
  3. Header Length (4 bytes): A little-endian integer specifying the exact byte length of the upcoming CRX3 header.
  4. CRX3 Header (Variable length): A binary data block serialized using Google's Protocol Buffers (Protobuf) that holds public keys, signatures, and extension identity data.
  5. Archive Payload (Variable length): The standard ZIP archive containing the extension's source files, assets, and the manifest.json file.

Packaging and Identity Generation

The packaging process begins by compressing the extension’s directory into a standard ZIP file. Next, the packaging tool uses a private cryptographic key (either RSA-2048 or ECDSA P-256) to establish the extension's identity.

Chromium derives the extension's unique CRX ID from the public key corresponding to this private key. It computes a SHA-256 hash of the public key (formatted as a SubjectPublicKeyInfo structure) and takes the first 16 bytes (128 bits) of that hash. In the browser UI, these 16 bytes are encoded into a 32-character string using the letters a through p instead of standard hexadecimal characters.

During packaging, the raw 16-byte CRX ID is placed inside a Protobuf message called SignedData.

The Cryptographic Signing Process

To prevent tampering, the CRX3 format signs both the extension's identity metadata and the ZIP archive together. Rather than signing the ZIP file in isolation, the packager constructs a specific byte stream to feed into the SHA-256 signature algorithm. This stream consists of:

  • A static ASCII prefix string: CRX3 SignedData\x00 (including the null terminator).
  • The 4-byte little-endian length of the serialized SignedData message.
  • The serialized SignedData Protobuf message (which holds the 16-byte CRX ID).
  • The entire ZIP archive payload.

The packager hashes this combined stream with SHA-256 and signs the hash using the private key. The resulting cryptographic signature and the accompanying public key are bundled into a Protobuf structure called an AsymmetricKeyProof.

Finally, the AsymmetricKeyProof and the SignedData are combined into the master CrxFileHeader Protobuf message. Because the header accepts multiple proofs, a CRX3 file can hold signatures from both the original developer and the Chrome Web Store simultaneously. The packager writes the magic number, version, header size, serialized CrxFileHeader, and ZIP payload to the disk to complete the CRX3 file.

Integrity Verification During Installation

When a user attempts to install a CRX3 file, Chromium runs a strict verification process before it extracts any files from the ZIP payload:

  1. Format Validation: Chromium reads the first 12 bytes to ensure the Cr24 magic number is present, the version is 3, and the header length is valid without exceeding the total file size.
  2. Header Deserialization: The browser reads the byte range specified by the header length and parses the CrxFileHeader Protobuf. If the Protobuf is malformed, installation aborts immediately.
  3. Key-to-ID Binding Check: Chromium extracts the SignedData to read the expected 16-byte CRX ID. It then loops through the AsymmetricKeyProof entries in the header, hashes the provided public key using SHA-256, and confirms that the first 16 bytes of the hash match the CRX ID in the SignedData. This prevents an attacker from stripping a valid signature and replacing it with their own key pair.
  4. Signature Verification: Chromium reconstructs the signed data stream in memory using the CRX3 SignedData\x00 prefix, the SignedData length, the SignedData bytes, and the appended ZIP archive. It uses the public key from the header to verify the cryptographic signature against this data stream.
  5. Publisher Proof Verification: Depending on browser security policies, Chromium may also require a verified signature matching the hardcoded public key of the Chrome Web Store, proving the extension was officially distributed.

If a single byte in the ZIP payload or the SignedData header was altered after signing, the reconstructed hash will not match the decrypted signature. Chromium will detect this mismatch, reject the file as corrupted or tampered with, and halt the installation before the ZIP archive is ever unpacked.