Skip to content

Commit 09fd62d

Browse files
committed
init
0 parents  commit 09fd62d

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-0
lines changed

crx3-new

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env node
2+
3+
let crypto = require('crypto')
4+
let fs = require('fs')
5+
let Pbf = require('pbf')
6+
let crx3pb = require('./crx3_pb')
7+
8+
function main() {
9+
let pem = fs.readFileSync(process.argv[2])
10+
let zipdata = fs.readFileSync('/dev/stdin')
11+
let crx = new CrxFile(pem, zipdata)
12+
process.stdout.write(crx.creat())
13+
}
14+
15+
class CrxFile {
16+
constructor(pem, zipdata) {
17+
this.private_key = crypto.createPrivateKey(pem)
18+
this.public_key_der = crypto.createPublicKey(pem)
19+
.export({type: 'spki', format: 'der'})
20+
this.zipdata = zipdata
21+
}
22+
23+
id() {
24+
return crypto.createHash('sha256')
25+
.update(this.public_key_der).digest().slice(0, 16)
26+
}
27+
28+
signed_data() {
29+
let pb = new Pbf()
30+
crx3pb.SignedData.write({crx_id: this.id()}, pb)
31+
return pb.finish()
32+
}
33+
34+
sign() {
35+
let magic_str = "CRX3 SignedData\x00"
36+
let signed_data = this.signed_data()
37+
return crypto.createSign('sha256')
38+
.update(magic_str)
39+
.update(octets(signed_data))
40+
.update(signed_data)
41+
.update(this.zipdata)
42+
.sign(this.private_key)
43+
}
44+
45+
header() {
46+
let pb = new Pbf()
47+
crx3pb.CrxFileHeader.write({
48+
sha256_with_rsa: [{ // AsymmetricKeyProof
49+
public_key: this.public_key_der,
50+
signature: this.sign()
51+
}],
52+
signed_header_data: this.signed_data()
53+
}, pb)
54+
return pb.finish()
55+
}
56+
57+
creat() {
58+
let magic_str = Buffer.from('Cr24')
59+
let version = octets('xxx')
60+
let header_size = octets(this.header())
61+
62+
return Buffer.concat([magic_str, version, header_size, this.header(),
63+
this.zipdata])
64+
}
65+
}
66+
67+
function octets(str) { // 4 bytes, little-endian
68+
let buf = Buffer.allocUnsafe(4)
69+
buf.writeUInt32LE(str.length, 0)
70+
return buf
71+
}
72+
73+
main()

crx3.proto

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file
4+
5+
syntax = "proto2";
6+
7+
option optimize_for = LITE_RUNTIME;
8+
9+
package crx_file;
10+
11+
// A CRX₃ file is a binary file of the following format:
12+
// [4 octets]: "Cr24", a magic number.
13+
// [4 octets]: The version of the *.crx file format used (currently 3).
14+
// [4 octets]: N, little-endian, the length of the header section.
15+
// [N octets]: The header (the binary encoding of a CrxFileHeader).
16+
// [M octets]: The ZIP archive.
17+
// Clients should reject CRX₃ files that contain an N that is too large for the
18+
// client to safely handle in memory.
19+
20+
message CrxFileHeader {
21+
// PSS signature with RSA public key. The public key is formatted as a
22+
// X.509 SubjectPublicKeyInfo block, as in CRX₂. In the common case of a
23+
// developer key proof, the first 128 bits of the SHA-256 hash of the
24+
// public key must equal the crx_id.
25+
repeated AsymmetricKeyProof sha256_with_rsa = 2;
26+
27+
// ECDSA signature, using the NIST P-256 curve. Public key appears in
28+
// named-curve format.
29+
// The pinned algorithm will be this, at least on 2017-01-01.
30+
repeated AsymmetricKeyProof sha256_with_ecdsa = 3;
31+
32+
// The binary form of a SignedData message. We do not use a nested
33+
// SignedData message, as handlers of this message must verify the proofs
34+
// on exactly these bytes, so it is convenient to parse in two steps.
35+
//
36+
// All proofs in this CrxFile message are on the value
37+
// "CRX3 SignedData\x00" + signed_header_size + signed_header_data +
38+
// archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData"
39+
// is encoded using UTF-8, signed_header_size is the size in octets of the
40+
// contents of this field and is encoded using 4 octets in little-endian
41+
// order, signed_header_data is exactly the content of this field, and
42+
// archive is the remaining contents of the file following the header.
43+
optional bytes signed_header_data = 10000;
44+
}
45+
46+
message AsymmetricKeyProof {
47+
optional bytes public_key = 1;
48+
optional bytes signature = 2;
49+
}
50+
51+
message SignedData {
52+
// This is simple binary, not UTF-8 encoded mpdecimal; i.e. it is exactly
53+
// 16 bytes long.
54+
optional bytes crx_id = 1;
55+
}

crx3_pb.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict'; // code generated by pbf v3.2.0
2+
3+
// CrxFileHeader ========================================
4+
5+
var CrxFileHeader = exports.CrxFileHeader = {};
6+
7+
CrxFileHeader.read = function (pbf, end) {
8+
return pbf.readFields(CrxFileHeader._readField, {sha256_with_rsa: [], sha256_with_ecdsa: [], signed_header_data: null}, end);
9+
};
10+
CrxFileHeader._readField = function (tag, obj, pbf) {
11+
if (tag === 2) obj.sha256_with_rsa.push(AsymmetricKeyProof.read(pbf, pbf.readVarint() + pbf.pos));
12+
else if (tag === 3) obj.sha256_with_ecdsa.push(AsymmetricKeyProof.read(pbf, pbf.readVarint() + pbf.pos));
13+
else if (tag === 10000) obj.signed_header_data = pbf.readBytes();
14+
};
15+
CrxFileHeader.write = function (obj, pbf) {
16+
if (obj.sha256_with_rsa) for (var i = 0; i < obj.sha256_with_rsa.length; i++) pbf.writeMessage(2, AsymmetricKeyProof.write, obj.sha256_with_rsa[i]);
17+
if (obj.sha256_with_ecdsa) for (i = 0; i < obj.sha256_with_ecdsa.length; i++) pbf.writeMessage(3, AsymmetricKeyProof.write, obj.sha256_with_ecdsa[i]);
18+
if (obj.signed_header_data) pbf.writeBytesField(10000, obj.signed_header_data);
19+
};
20+
21+
// AsymmetricKeyProof ========================================
22+
23+
var AsymmetricKeyProof = exports.AsymmetricKeyProof = {};
24+
25+
AsymmetricKeyProof.read = function (pbf, end) {
26+
return pbf.readFields(AsymmetricKeyProof._readField, {public_key: null, signature: null}, end);
27+
};
28+
AsymmetricKeyProof._readField = function (tag, obj, pbf) {
29+
if (tag === 1) obj.public_key = pbf.readBytes();
30+
else if (tag === 2) obj.signature = pbf.readBytes();
31+
};
32+
AsymmetricKeyProof.write = function (obj, pbf) {
33+
if (obj.public_key) pbf.writeBytesField(1, obj.public_key);
34+
if (obj.signature) pbf.writeBytesField(2, obj.signature);
35+
};
36+
37+
// SignedData ========================================
38+
39+
var SignedData = exports.SignedData = {};
40+
41+
SignedData.read = function (pbf, end) {
42+
return pbf.readFields(SignedData._readField, {crx_id: null}, end);
43+
};
44+
SignedData._readField = function (tag, obj, pbf) {
45+
if (tag === 1) obj.crx_id = pbf.readBytes();
46+
};
47+
SignedData.write = function (obj, pbf) {
48+
if (obj.crx_id) pbf.writeBytesField(1, obj.crx_id);
49+
};

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"pbf": "^3.2.0"
4+
}
5+
}

0 commit comments

Comments
 (0)