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.
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:
- Magic number
- SignedData instance length
- SignedData (contains the id)
- 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