¿Cómo calcular programáticamente la extensión de Chrome?

Estoy construyendo un proceso automatizado para producir extensiones. ¿Hay un ejemplo de código para calcular la extensión-ID directamente y sin pasar por la interacción con el navegador?

(Estoy respondiendo a mi propia pregunta, abajo.)

Solo pude encontrar un artículo relacionado con un fragmento de Ruby, y solo está disponible en la IA: http://web.archive.org/web/20120606044635/http://supercollider.dk/2010/01/calculating- chrome-extension-id-from-your-private-key-233

Importante saber:

  1. Esto depende de una clave pública codificada en DER (binario sin formato), no de una clave codificada en PEM (ASCII agradable generado por base64 que codifica la clave DER).
  2. Los ID de extensión son base-16, pero están codificados usando [ap] (llamado “mpdecimal”), en lugar de [0-9a-f].

Usando una clave pública codificada PEM, siga los siguientes pasos:

  1. Si su clave pública con formato PEM aún tiene el encabezado y el pie de página y está dividida en varias líneas, reformatéela manualmente para que tenga una sola cadena de caracteres que excluya el encabezado y el pie de página, y se ejecuten juntos de modo que cada línea del clave se ajusta a la siguiente.
  2. Base64-decodifica la clave pública para generar una clave pública con formato DER.
  3. Genere un resumen de SHA256 hexadecimal de la clave con formato DER.
  4. Toma los primeros 32 bytes del hash. No necesitarás el rest.
  5. Para cada carácter, conviértalo a base-10 y agregue el código ASCII para ‘a’.

La siguiente es una rutina de Python para hacer esto:

import hashlib from base64 import b64decode def build_id(pub_key_pem): pub_key_der = b64decode(pub_key_pem) sha = hashlib.sha256(pub_key_der).hexdigest() prefix = sha[:32] reencoded = "" ord_a = ord('a') for old_char in prefix: code = int(old_char, 16) new_char = chr(ord_a + code) reencoded += new_char return reencoded def main(): pub_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjvF5pjuK8gRaw/2LoRYi37QqRd48B/FeO9yFtT6ueY84z/u0NrJ/xbPFc9OCGBi8RKIblVvcbY0ySGqdmp0QsUr/oXN0b06GL4iB8rMhlO082HhMzrClV8OKRJ+eJNhNBl8viwmtJs3MN0x9ljA4HQLaAPBA9a14IUKLjP0pWuwIDAQAB' id_ = build_id(pub_key) print(id_) if __name__ == '__main__': main() 

Eres más que bienvenido a probar esto contra una extensión existente y su ID. Para recuperar su clave pública con formato PEM:

  1. Ve a la lista de tus extensiones existentes en Chrome. Agarra la extensión-ID de uno.
  2. Encuentra el directorio donde está alojada la extensión. En mi caja de Windows 7, es: C: \ Users \\ AppData \ Local \ Google \ Chrome \ User Data \ Default \ Extensions \
  3. Agarra la clave pública del archivo manifest.json debajo de “clave”. Como la clave ya está lista para ser descodificada en base64, puede omitir el paso (1) del proceso.

La clave pública en el ejemplo es de la extensión “Chrome Reader”. Su ID de extensión es “lojpenhmoajbiciapkjkiekmobleogjc”.

Ver también:

  1. Google Chrome – hashes alfanuméricos para identificar extensiones
  2. http://blog.roomanna.com/12-14-2010/getting-an-extensions-id

A partir de Chrome 64, Chrome cambió el formato del paquete para las extensiones al formato de archivo CRX₃ , que admite múltiples firmas y declara explícitamente su ID de CRX. Para extraer el ID de CRX de un archivo CRX₃ es necesario analizar un búfer de protocolo.

Aquí hay una pequeña secuencia de comandos de Python para extraer la ID de un archivo CRX₃. Esta solución solo se debe utilizar con archivos CRX trusted de confianza o en contextos donde la seguridad no es un problema: a diferencia de CRX₂, el formato del paquete no restringe lo que CRX ID declara en un archivo CRX₃. (En la práctica, los consumidores del archivo (es decir, Chrome) lo impondrán, por ejemplo, exigir que el archivo se firme con al menos una clave que incluya el ID de CRX declarado).

 import binascii import string import struct import sys def decode(proto, data): index = 0 length = len(data) msg = dict() while index < length: item = 128 key = 0 left = 0 while item & 128: item = data[index] index += 1 value = (item & 127) << left key += value left += 7 field = key >> 3 wire = key & 7 if wire == 0: item = 128 num = 0 left = 0 while item & 128: item = data[index] index += 1 value = (item & 127) << left num += value left += 7 continue elif wire == 1: index += 8 continue elif wire == 2: item = 128 _length = 0 left = 0 while item & 128: item = data[index] index += 1 value = (item & 127) << left _length += value left += 7 last = index index += _length item = data[last:index] if field not in proto: continue msg[proto[field]] = item continue elif wire == 5: index += 4 continue raise ValueError( 'invalid wire type: {wire}'.format(wire=wire) ) return msg def get_extension_id(crx_file): with open(crx_file, 'rb') as f: f.read(8); # 'Cr24\0\0\0\3' data = f.read(struct.unpack(' 

(Gracias a https://github.com/thelinuxkid/python-protolite por el esqueleto del analizador protobuf).

Una forma agradable y sencilla de obtener la clave pública del archivo .crx con python, ya que Chrome solo genera la clave privada .pem para ti. La clave pública se almacena realmente en el archivo .crx.

Esto se basa en el formato del archivo .crx que se encuentra aquí http://developer.chrome.com/extensions/crx.html

 import struct import hashlib import string def get_pub_key_from_crx(crx_file): with open(crx_file, 'rb') as f: data = f.read() header = struct.unpack('<4sIII', data[:16]) pubkey = struct.unpack('<%ds' % header[2], data[16:16+header[2]])[0] return pubkey def get_extension_id(crx_file): pubkey = get_pub_key_from_crx(crx_file) digest = hashlib.sha256(pubkey).hexdigest() trans = string.maketrans('0123456789abcdef', string.ascii_lowercase[:16]) return string.translate(digest[:32], trans) if __name__ == '__main__': import sys if len(sys.argv) != 2: print 'usage: %s crx_file' % sys.argv[0] print get_extension_id(sys.argv[1]) 

Aunque esto no es posible hacerlo "omitiendo la interacción con el navegador", porque todavía necesita generar el archivo .crx con un comando como

 chrome.exe --pack-extension=my_extension --pack-extension-key=my_extension.pem