Re-enable index-basics-workers test to see if still times
[chromium-blink-merge.git] / tools / crx_id / crx_id.py
blob5f8e2b227907fde1f204e14ef8f7ee696f9f695c
1 #!/usr/bin/env python
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.
11 """
13 import base64
14 import os
15 import sys
16 import hashlib
18 try:
19 import json
20 except Exception:
21 import simplejson as json
23 EXPECTED_CRX_MAGIC_NUM = 'Cr24'
24 EXPECTED_CRX_VERSION = 2
26 def usage(argv):
27 print "%s: crx_file" % argv[0]
29 def HexToInt(hex_chars):
30 """ Convert bytes like \xab -> 171 """
31 val = 0
32 for i in xrange(len(hex_chars)):
33 val += pow(256, i) * ord(hex_chars[i])
34 return val
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.
39 """
40 result = ''
41 base = ord('a')
42 for i in xrange(len(hex_chars)):
43 value = ord(hex_chars[i])
44 dig1 = value / 16
45 dig2 = value % 16
46 result += chr(dig1 + base)
47 result += chr(dig2 + base)
48 return result
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[] = { ... }; """
54 result = []
55 for i in xrange(len(hex_chars)):
56 value = ord(hex_chars[i])
57 dig1 = value / 16
58 dig2 = value % 16
59 result.append('0x' + hex(dig1)[2:] + hex(dig2)[2:])
60 return '{%s}' % ', '.join(result)
62 def GetPublicKeyPacked(f):
63 magic_num = f.read(4)
64 if magic_num != EXPECTED_CRX_MAGIC_NUM:
65 raise Exception('Invalid magic number: %s (expecting %s)' %
66 (magic_num,
67 EXPECTED_CRX_MAGIC_NUM))
68 version = f.read(4)
69 if not version[0] != EXPECTED_CRX_VERSION:
70 raise Exception('Invalid version number: %s (expecting %s)' %
71 (version,
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)
76 return pub_key
78 def GetPublicKeyFromPath(filepath):
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
85 filepath[1] == ':'):
86 return filepath[0].upper() + filepath[1:]
87 return filepath
89 def GetPublicKeyUnpacked(f, filepath):
90 manifest = json.load(f)
91 if 'key' not in manifest:
92 # Use the path as the public key.
93 # See Extension::GenerateIdForPath in extension.cc
94 return GetPublicKeyFromPath(filepath)
95 else:
96 return base64.standard_b64decode(manifest['key'])
98 def HasPublicKey(filename):
99 if os.path.isdir(filename):
100 with open(os.path.join(filename, 'manifest.json'), 'rb') as f:
101 manifest = json.load(f)
102 return 'key' in manifest
103 return False
105 def GetPublicKey(filename, from_file_path):
106 if from_file_path:
107 return GetPublicKeyFromPath(filename)
109 pub_key = ''
110 if os.path.isdir(filename):
111 # Assume it's an unpacked extension
112 f = open(os.path.join(filename, 'manifest.json'), 'rb')
113 pub_key = GetPublicKeyUnpacked(f, filename)
114 f.close()
115 else:
116 # Assume it's a packed extension.
117 f = open(filename, 'rb')
118 pub_key = GetPublicKeyPacked(f)
119 f.close()
120 return pub_key
122 def GetCRXHash(filename, from_file_path=False):
123 pub_key = GetPublicKey(filename, from_file_path)
124 pub_key_hash = hashlib.sha256(pub_key).digest()
125 return HexTo256(pub_key_hash)
127 def GetCRXAppID(filename, from_file_path=False):
128 pub_key = GetPublicKey(filename, from_file_path)
129 pub_key_hash = hashlib.sha256(pub_key).digest()
130 # AppID is the MPDecimal of only the first 128 bits of the hash.
131 return HexToMPDecimal(pub_key_hash[:128/8])
133 def main(argv):
134 if len(argv) != 2:
135 usage(argv)
136 return 1
137 print 'Raw Bytes: %s' % GetCRXHash(sys.argv[1])
138 print 'AppID: %s' % GetCRXAppID(sys.argv[1])
141 if __name__ == '__main__':
142 sys.exit(main(sys.argv))