Alexander Gromnitsky's Blog

CRX3

Latest update:

Starting with version 73, Chrome has switched the required package format for extensions to crx3. Why new file format?

Date: Fri, 20 Jan 2017 17:10:24 -0800
From: Joshua Pawlicki <waffles@chromium.org>
To: chromium-dev@chromium.org
Subject: Intent to Implement CRX₃
Message-ID: <CAFE=Dz0-aG-w+iA=P6JRL6NmBMVPhAHKeD8diPa72JjELGPzzw@mail.gmail.com>

[...] Chrome extensions are currently packaged for installation/update
as signed zip files called CRX₂ files, using SHA1withRSA for the
signature algorithm.  Many of the RSA keys used to sign the files are
insufficiently secure (too short). The CRX₂ format does not allow for
algorithm rotation, key rotation, or multiple proofs. The goal is to
address these issues and leave the door open to future improvements. [...]

Chrome even allows to create a .crx file from the command line (e.g., in Linux: google-chrome --pack-extension=ext-dir --pack-extension-key=file.pem).

What if you'd like to create .crx files on a server that doesn't have Chrome installed?

A brief intro to Crx3

The crx2 file format was very simple: you made an sha1 of a zip file, signed it with an RSA private key & prepended the public key & the signature to the zip archive.

Crx3 prepends a protobuf that can contain an unlimited number of public_key+signature tuples (also called proofs). If you create a .crx file by yourself for a Linux version of Chrome, only 1 proof is required. Extensions from the Chrome Web Store incorporate multiple proofs.

Switching to protobufs also means you need a proper protobuf parser to be able read the new file format.

crx3 file format diagram

To see this in action, let's create a noop extension that consists only from manifest.json file.

$ cat manifest.json
{
    "manifest_version": 2,
    "name": "foo",
    "version": "1.2.3"
}
$ zip foo.zip manifest.json
  adding: manifest.json (deflated 25%)

Create an RSA public key in the PEM format:

$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
..................................+++++
.......+++++

Then do npm -g i crx3-utils (a standard disclaimer) & make a .crx:

$ crx3-new private.pem < foo.zip > foo.crx
$ file foo.crx
foo.crx: Google Chrome extension, version 3

You can now drop it into chrome://extensions/ page & Chrome should happily accept it.

To see what's inside the foo.crx, run:

$ crx3-info < foo.crx
id                   jnedgebbcnmoemphjanchkhfkjjhmael
header               593
payload              231
sha256_with_rsa      1 main_idx=0
sha256_with_ecdsa    0

payload is the size of the original zip archive.

id is the extension id that was calculated during the crx file creation. Here, the public key, from which the calculation was done, is in sha256_with_rsa list (alongside with a signature, both in a tuple under the index 0). If we add another tuple (proof) to the crx file, this time using a different private key, the id won't change, for it's permanently saved in SignedData protobuf structure (see the diagram above). This is very different from crx2, where you had to extract a public key first & then calculate the id from it.

In crx2, the signature was just an sha1 of a zip file. In crx3, each proof signs the following sequence of data:

  1. Magic number
  2. SignedData instance length
  3. SignedData (contains the id)
  4. Zip archive

Web Store

If we download whatever extension from the Web Store (say, Google Dictionary), it'll hold 3 proofs inside:

$ curl -sL 'https://clients2.google.com/service/update2/crx?response=redirect&prodversion=73.0.3683.86&x=id%3Dmgijmajocgfcbeboacabfgobmjgjcoja%26uc&acceptformat=crx3' > google-dictionary.crx

$ /crx3-info < google-dictionary.crx
id                   mgijmajocgfcbeboacabfgobmjgjcoja
header               1061
payload              44018
sha256_with_rsa      2 main_idx=1
sha256_with_ecdsa    1

sha256_with_rsa has an additional public_key+signature tuple, the public key from which is shared between all the extension from the Web Store. The original proof (from the 'developer' key) is shifted to the end of sha256_with_rsa list.

sha256_with_ecdsa is another tuple to which only Google has the private key.


Tags: ойті
Authors: ag