@@ -7,13 +7,15 @@ import (
77 "crypto"
88 "crypto/rand"
99 "crypto/rsa"
10- "crypto/sha1 "
10+ "crypto/sha256 "
1111 "crypto/x509"
1212 "encoding/base64"
1313 "encoding/binary"
1414 "io"
1515 "os"
1616
17+ "github.com/golang/protobuf/proto"
18+ "github.com/mediabuyerbot/go-crx3/pb"
1719 "github.com/tebeka/selenium/internal/zip"
1820)
1921
@@ -179,23 +181,12 @@ func NewExtension(basePath string) ([]byte, *rsa.PrivateKey, error) {
179181// NewExtensionWithKey creates the payload of a Chrome extension file which is
180182// signed by the provided private key.
181183func NewExtensionWithKey (basePath string , key * rsa.PrivateKey ) ([]byte , error ) {
182- zip , err := zip .New (basePath )
184+ archiveBuf , err := zip .New (basePath )
183185 if err != nil {
184186 return nil , err
185187 }
186188
187- h := sha1 .New ()
188- if _ , err := io .Copy (h , bytes .NewReader (zip .Bytes ())); err != nil {
189- return nil , err
190- }
191- hashed := h .Sum (nil )
192-
193- signature , err := rsa .SignPKCS1v15 (rand .Reader , key , crypto .SHA1 , hashed [:])
194- if err != nil {
195- return nil , err
196- }
197-
198- pubKey , err := x509 .MarshalPKIXPublicKey (key .Public ())
189+ header , err := crx3Header (archiveBuf .Bytes (), key )
199190 if err != nil {
200191 return nil , err
201192 }
@@ -207,33 +198,86 @@ func NewExtensionWithKey(basePath string, key *rsa.PrivateKey) ([]byte, error) {
207198 }
208199
209200 // Version.
210- if err := binary .Write (buf , binary .LittleEndian , uint32 (2 )); err != nil {
201+ if err := binary .Write (buf , binary .LittleEndian , uint32 (3 )); err != nil {
211202 return nil , err
212203 }
213204
214- // Public key length.
215- if err := binary .Write (buf , binary .LittleEndian , uint32 (len (pubKey ))); err != nil {
205+ // header length.
206+ if err := binary .Write (buf , binary .LittleEndian , uint32 (len (header ))); err != nil {
216207 return nil , err
217208 }
218- // Signature length .
219- if err := binary .Write (buf , binary .LittleEndian , uint32 ( len ( signature )) ); err != nil {
209+ // header payload .
210+ if err := binary .Write (buf , binary .LittleEndian , header ); err != nil {
220211 return nil , err
221212 }
222213
223- // Public key payload.
224- if err := binary .Write (buf , binary .LittleEndian , pubKey ); err != nil {
214+ // Zipped extension directory payload.
215+ if err := binary .Write (buf , binary .LittleEndian , archiveBuf . Bytes () ); err != nil {
225216 return nil , err
226217 }
218+ return buf .Bytes (), nil
219+ }
227220
228- // Signature payload.
229- if err := binary .Write (buf , binary .LittleEndian , signature ); err != nil {
221+ func crx3Header (archiveData []byte , key * rsa.PrivateKey ) ([]byte , error ) {
222+ // Public Key
223+ pubKey , err := x509 .MarshalPKIXPublicKey (key .Public ())
224+ if err != nil {
230225 return nil , err
231226 }
232227
233- // Zipped extension directory payload.
234- if err := binary .Write (buf , binary .LittleEndian , zip .Bytes ()); err != nil {
228+ // Signed Header
229+
230+ // From chromium / crx3.proto:
231+ //
232+ // In the common case of a developer key proof, the first 128 bits of
233+ // the SHA-256 hash of the public key must equal the crx_id.
234+ hash := sha256 .New ()
235+ hash .Write (pubKey )
236+ sdpb := & pb.SignedData {
237+ CrxId : hash .Sum (nil )[0 :16 ],
238+ }
239+ signedHeaderData , err := proto .Marshal (sdpb )
240+ if err != nil {
235241 return nil , err
236242 }
237243
238- return buf .Bytes (), nil
244+ // Signature
245+ signature , err := crx3Signature (archiveData , signedHeaderData , key )
246+ if err != nil {
247+ return nil , err
248+ }
249+
250+ header := & pb.CrxFileHeader {
251+ Sha256WithRsa : []* pb.AsymmetricKeyProof {
252+ & pb.AsymmetricKeyProof {
253+ PublicKey : pubKey ,
254+ Signature : signature ,
255+ },
256+ },
257+ SignedHeaderData : signedHeaderData ,
258+ }
259+ return proto .Marshal (header )
260+ }
261+
262+ func crx3Signature (archiveData , signedHeaderData []byte , key * rsa.PrivateKey ) ([]byte , error ) {
263+ // From chromium / crx3.proto:
264+ //
265+ // All proofs in this CrxFile message are on the value
266+ // "CRX3 SignedData\x00" + signed_header_size + signed_header_data +
267+ // archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData"
268+ // is encoded using UTF-8, signed_header_size is the size in octets of the
269+ // contents of this field and is encoded using 4 octets in little-endian
270+ // order, signed_header_data is exactly the content of this field, and
271+ // archive is the remaining contents of the file following the header.
272+
273+ sign := sha256 .New ()
274+ sign .Write ([]byte ("CRX3 SignedData\x00 " ))
275+ if err := binary .Write (sign , binary .LittleEndian , uint32 (len (signedHeaderData ))); err != nil {
276+ return nil , err
277+ }
278+ sign .Write (signedHeaderData )
279+ if _ , err := io .Copy (sign , bytes .NewReader (archiveData )); err != nil {
280+ return nil , err
281+ }
282+ return rsa .SignPKCS1v15 (rand .Reader , key , crypto .SHA256 , sign .Sum (nil ))
239283}
0 commit comments