peerigon / unzip-crx

Unzip chrome extension files
MIT License
52 stars 18 forks source link

Manifest key is missing #15

Open Amaimersion opened 5 months ago

Amaimersion commented 5 months ago

If you manually install extension from Chrome Web Store, then unzipped manifest.json will contain key property. This key is needed to sign CRX and produce constant extension ID. During unzipping Google Chrome automatically extracts public key from CRX, adds it to manifest.json and calculates extension ID. Example:

{
   "manifest_version": 3,
   "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtAltq/HtarGbnR3hzjUeyBU5X1giAakPpIuBsjl6LRTklBHNETfi+aWA5BSq//xKnPbnmA5lHtxQWSh9E9tEKjxrdWeUwIsFBQaCFH2n25zihKhlkO50OD8iCJ6fwsJu4mTsSpmNJLrF8z4HLTrEjQPiJB6j/oNxldNKPuEbZfQIDAQAB",
}

If you use unzip-crx to unzip CRX, you will notice that manifest.json is missing key. Example:

{
   "manifest_version": 3
}

I don't sure if this project should be responsible for extracting key, but during development I encountered a need for the right key extraction, so I will submit the issue here. It would be nice to add it as a part of this project. But if it is out of scope, then at least add it in README.

This project https://github.com/gromnitsky/crx3-utils implements key extraction using Node. Usage:

import { parse, rsa_main_index, der2pem, u } from "./index.js";

const file = "downloaded.crx";

const buff = await u.read(file);
const hdr = parse(buff);
const index = rsa_main_index(hdr);
const proof = hdr.sha256_with_rsa[index];
const key = der2pem(proof.public_key);

let value = key;
value = value.replace("-----BEGIN PUBLIC KEY-----", "");
value = value.replace("-----END PUBLIC KEY-----", "");
value = value.replaceAll("\n", "");

console.log(value); // MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtAltq/HtarGbnR3hzjUeyBU5X1giAakPpIuBsjl6LRTklBHNETfi+aWA5BSq//xKnPbnmA5lHtxQWSh9E9tEKjxrdWeUwIsFBQaCFH2n25zihKhlkO50OD8iCJ6fwsJu4mTsSpmNJLrF8z4HLTrEjQPiJB6j/oNxldNKPuEbZfQIDAQAB

You also can use it as a child process (example for Linux):


import child_process from "child_process";

const file = "downloaded.crx";

const cmdIndex = `node ./crx3-info < ${file} | grep main_idx | cut -d "=" -f 2`;
const index = child_process.execSync(cmdIndex).toString().trim();

const cmdKey = `node ./crx3-info rsa ${index} < ${file} | tail -n +2 | head -n -1 | paste -sd ""`;
const key = child_process.execSync(cmdKey).toString().trim();

console.log(key); // MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtAltq/HtarGbnR3hzjUeyBU5X1giAakPpIuBsjl6LRTklBHNETfi+aWA5BSq//xKnPbnmA5lHtxQWSh9E9tEKjxrdWeUwIsFBQaCFH2n25zihKhlkO50OD8iCJ6fwsJu4mTsSpmNJLrF8z4HLTrEjQPiJB6j/oNxldNKPuEbZfQIDAQAB

Both ways results in a key value that I provided at the beginning. Now you can change content of manifest.json and add key property with a correct value.

Additional links:

jhnns commented 5 months ago

Hi @Amaimersion 👋. Thank you for that detailed explanation. I don't think Including the key is out of scope. Would you be up for opening a PR using the first approach with the crx3-utils library?

Amaimersion commented 5 months ago

Sure. I will try to find free time and do it on this week