diff --git a/src/lib/crx-to-zip.js b/src/lib/crx-to-zip.js index 0e3b37c..ed7adc4 100644 --- a/src/lib/crx-to-zip.js +++ b/src/lib/crx-to-zip.js @@ -6,6 +6,7 @@ /* exported openCRXasZip */ /* jshint browser:true, devel:true */ +/* globals CryptoJS */ // For sha256 hash calculation 'use strict'; // Strips CRX headers from zip @@ -40,7 +41,7 @@ var CRXtoZIP = (function() { zipStartOffset = 16 + publicKeyLength + signatureLength; // Public key - publicKeyBase64 = getAsBase64(view, 16, 16 + publicKeyLength); + publicKeyBase64 = btoa(getBinaryString(view, 16, 16 + publicKeyLength)); } else { // view[4] === 3 // CRX3 - https://cs.chromium.org/chromium/src/components/crx_file/crx3.proto var crx3HeaderLength = calcLength(view[ 8], view[ 9], view[10], view[11]); @@ -67,12 +68,12 @@ var CRXtoZIP = (function() { length += d << 24 >>> 0; return length; } - function getAsBase64(bytesView, startOffset, endOffset) { + function getBinaryString(bytesView, startOffset, endOffset) { var binaryString = ''; for (var i = startOffset; i < endOffset; ++i) { binaryString += String.fromCharCode(bytesView[i]); } - return btoa(binaryString); + return binaryString; } function getPublicKeyFromProtoBuf(bytesView, startOffset, endOffset) { // Protobuf definition: https://cs.chromium.org/chromium/src/components/crx_file/crx3.proto @@ -81,6 +82,9 @@ var CRXtoZIP = (function() { // To find the public key: // 1. Look for CrxFileHeader.sha256_with_rsa (field number 2). // 2. Look for AsymmetricKeyProof.public_key (field number 1). + // 3. Look for CrxFileHeader.signed_header_data (SignedData.crx_id). + // This has 16 bytes (128 bits). Verify that those match with the + // first 128 bits of the sha256 hash of the chosen public key. function getvarint() { // Note: We don't do bound checks (startOffset < endOffset) here, @@ -100,18 +104,38 @@ var CRXtoZIP = (function() { return val; } + var publicKeys = []; + var crxIdBin; while (startOffset < endOffset) { var key = getvarint(); var length = getvarint(); + if (key === 80002) { // This is ((10000 << 3) | 2) (signed_header_data). + var sigdatakey = getvarint(); + var sigdatalen = getvarint(); + if (sigdatakey !== 0xA) { + console.warn('proto: Unexpected key in signed_header_data: ' + sigdatakey); + } else if (sigdatalen !== 16) { + console.warn('proto: Unexpected signed_header_data length ' + length); + } else if (crxIdBin) { + console.warn('proto: Unexpected duplicate signed_header_data'); + } else { + crxIdBin = bytesView.subarray(startOffset, startOffset + 16); + } + startOffset += sigdatalen; + continue; + } if (key !== 0x12) { - // Likely 0x1a (sha256_with_ecdsa) or 0x82f104 (signed_header_data). + // Likely 0x1a (sha256_with_ecdsa). + if (key != 0x1a) { + console.warn('proto: Unexpected key: ' + key); + } startOffset += length; continue; } // Found 0x12 (sha256_with_rsa); Look for 0xA (public_key). + var keyproofend = startOffset + length; var keyproofkey = getvarint(); var keyprooflength = getvarint(); - var keyproofend = startOffset + length; // AsymmetricKeyProof could contain 0xA (public_key) or 0x12 (signature). if (keyproofkey === 0x12) { startOffset += keyprooflength; @@ -132,8 +156,25 @@ var CRXtoZIP = (function() { break; } // Found 0xA (public_key). - return getAsBase64(bytesView, startOffset, startOffset + keyprooflength); + publicKeys.push(getBinaryString(bytesView, startOffset, startOffset + keyprooflength)); + startOffset = keyproofend; + } + if (!publicKeys.length) { + console.warn('proto: Did not find any public key'); + return; + } + if (!crxIdBin) { + console.warn('proto: Did not find crx_id'); + return; + } + var crxIdHex = CryptoJS.enc.Latin1.parse(getBinaryString(crxIdBin, 0, 16)).toString(); + for (var i = 0; i < publicKeys.length; ++i) { + var sha256sum = CryptoJS.SHA256(CryptoJS.enc.Latin1.parse(publicKeys[i])).toString(); + if (sha256sum.slice(0, 32) === crxIdHex) { + return btoa(publicKeys[i]); + } } + console.warn('proto: None of the public keys matched with crx_id'); } return CRXtoZIP; })(); diff --git a/src/popup.html b/src/popup.html index 513ee1a..e76046a 100644 --- a/src/popup.html +++ b/src/popup.html @@ -43,6 +43,7 @@ +