2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """ Read a CRX file and write out the App ID and the Full Hash of the ID.
7 See: http://code.google.com/chrome/extensions/crx.html
8 and 'http://stackoverflow.com/questions/'
9 + '1882981/google-chrome-alphanumeric-hashes-to-identify-extensions'
10 for docs on the format.
21 import simplejson
as json
23 EXPECTED_CRX_MAGIC_NUM
= 'Cr24'
24 EXPECTED_CRX_VERSION
= 2
27 print "%s: crx_file" % argv
[0]
29 def HexToInt(hex_chars
):
30 """ Convert bytes like \xab -> 171 """
32 for i
in xrange(len(hex_chars
)):
33 val
+= pow(256, i
) * ord(hex_chars
[i
])
36 def HexToMPDecimal(hex_chars
):
37 """ Convert bytes to an MPDecimal string. Example \x00 -> "aa"
38 This gives us the AppID for a chrome extension.
42 for i
in xrange(len(hex_chars
)):
43 value
= ord(hex_chars
[i
])
46 result
+= chr(dig1
+ base
)
47 result
+= chr(dig2
+ base
)
50 def HexTo256(hex_chars
):
51 """ Convert bytes to pairs of hex digits. E.g., \x00\x11 -> "{0x00, 0x11}"
52 The format is taylored for copy and paste into C code:
53 const uint8 sha256_hash[] = { ... }; """
55 for i
in xrange(len(hex_chars
)):
56 value
= ord(hex_chars
[i
])
59 result
.append('0x' + hex(dig1
)[2:] + hex(dig2
)[2:])
60 return '{%s}' % ', '.join(result
)
62 def GetPublicKeyPacked(f
):
64 if magic_num
!= EXPECTED_CRX_MAGIC_NUM
:
65 raise Exception('Invalid magic number: %s (expecting %s)' %
67 EXPECTED_CRX_MAGIC_NUM
))
69 if not version
[0] != EXPECTED_CRX_VERSION
:
70 raise Exception('Invalid version number: %s (expecting %s)' %
72 EXPECTED_CRX_VERSION
))
73 pub_key_len_bytes
= HexToInt(f
.read(4))
74 sig_len_bytes
= HexToInt(f
.read(4))
75 pub_key
= f
.read(pub_key_len_bytes
)
78 def GetPublicKeyFromPath(filepath
, is_win_path
=False):
79 # Normalize the path for windows to have capital drive letters.
80 # We intentionally don't check if sys.platform == 'win32' and just
81 # check if this looks like drive letter so that we can test this
82 # even on posix systems.
83 if (len(filepath
) >= 2 and
84 filepath
[0].islower() and
86 filepath
= filepath
[0].upper() + filepath
[1:]
88 # On Windows, filepaths are encoded using UTF-16, little endian byte order,
89 # using "wide characters" that are 16 bits in size. On POSIX systems, the
90 # encoding is generally UTF-8, which has the property of being equivalent to
91 # ASCII when only ASCII characters are in the path.
93 filepath
= filepath
.encode('utf-16le')
97 def GetPublicKeyUnpacked(f
, filepath
):
98 manifest
= json
.load(f
)
99 if 'key' not in manifest
:
100 # Use the path as the public key.
101 # See Extension::GenerateIdForPath in extension.cc
102 return GetPublicKeyFromPath(filepath
)
104 return base64
.standard_b64decode(manifest
['key'])
106 def HasPublicKey(filename
):
107 if os
.path
.isdir(filename
):
108 with
open(os
.path
.join(filename
, 'manifest.json'), 'rb') as f
:
109 manifest
= json
.load(f
)
110 return 'key' in manifest
113 def GetPublicKey(filename
, from_file_path
, is_win_path
=False):
115 return GetPublicKeyFromPath(
116 filename
, is_win_path
=is_win_path
)
119 if os
.path
.isdir(filename
):
120 # Assume it's an unpacked extension
121 f
= open(os
.path
.join(filename
, 'manifest.json'), 'rb')
122 pub_key
= GetPublicKeyUnpacked(f
, filename
)
125 # Assume it's a packed extension.
126 f
= open(filename
, 'rb')
127 pub_key
= GetPublicKeyPacked(f
)
131 def GetCRXHash(filename
, from_file_path
=False, is_win_path
=False):
132 pub_key
= GetPublicKey(filename
, from_file_path
, is_win_path
=is_win_path
)
133 pub_key_hash
= hashlib
.sha256(pub_key
).digest()
134 return HexTo256(pub_key_hash
)
136 def GetCRXAppID(filename
, from_file_path
=False, is_win_path
=False):
137 pub_key
= GetPublicKey(filename
, from_file_path
, is_win_path
=is_win_path
)
138 pub_key_hash
= hashlib
.sha256(pub_key
).digest()
139 # AppID is the MPDecimal of only the first 128 bits of the hash.
140 return HexToMPDecimal(pub_key_hash
[:128/8])
146 print 'Raw Bytes: %s' % GetCRXHash(sys
.argv
[1])
147 print 'AppID: %s' % GetCRXAppID(sys
.argv
[1])
150 if __name__
== '__main__':
151 sys
.exit(main(sys
.argv
))