Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / tools / telemetry / third_party / webpagereplay / adb_install_cert.py
blob4f311b043b7f0088650ec1bc79a5dbaf63c07b24
1 # Copyright 2014 Google Inc. All Rights Reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """Installs certificate on phone with KitKat."""
17 import argparse
18 import logging
19 import os
20 import subprocess
21 import sys
23 KEYCODE_ENTER = '66'
24 KEYCODE_TAB = '61'
26 class CertInstallError(Exception):
27 pass
29 class CertRemovalError(Exception):
30 pass
33 class AndroidCertInstaller(object):
34 """Certificate installer for phones with KitKat."""
36 def __init__(self, device_id, cert_name, cert_path):
37 if not os.path.exists(cert_path):
38 raise ValueError('Not a valid certificate path')
39 self.device_id = device_id
40 self.cert_name = cert_name
41 self.cert_path = cert_path
42 self.file_name = os.path.basename(self.cert_path)
43 self.reformatted_cert_fname = None
44 self.reformatted_cert_path = None
45 self.android_cacerts_path = None
47 @staticmethod
48 def _run_cmd(cmd, dirname=None):
49 return subprocess.check_output(cmd, cwd=dirname)
51 def _adb(self, *args):
52 """Runs the adb command."""
53 cmd = ['adb']
54 if self.device_id:
55 cmd.extend(['-s', self.device_id])
56 cmd.extend(args)
57 return self._run_cmd(cmd)
59 def _adb_su_shell(self, *args):
60 """Runs command as root."""
61 cmd = ['shell', 'su', '-c']
62 cmd.extend(args)
63 return self._adb(*cmd)
65 def _get_property(self, prop):
66 return self._adb('shell', 'getprop', prop).strip()
68 def check_device(self):
69 install_warning = False
70 if self._get_property('ro.product.device') != 'hammerhead':
71 logging.warning('Device is not hammerhead')
72 install_warning = True
73 if self._get_property('ro.build.version.release') != '4.4.2':
74 logging.warning('Version is not 4.4.2')
75 install_warning = True
76 if install_warning:
77 logging.warning('Certificate may not install properly')
79 def _input_key(self, key):
80 """Inputs a keyevent."""
81 self._adb('shell', 'input', 'keyevent', key)
83 def _input_text(self, text):
84 """Inputs text."""
85 self._adb('shell', 'input', 'text', text)
87 @staticmethod
88 def _remove(file_name):
89 """Deletes file."""
90 if os.path.exists(file_name):
91 os.remove(file_name)
93 def _format_hashed_cert(self):
94 """Makes a certificate file that follows the format of files in cacerts."""
95 self._remove(self.reformatted_cert_path)
96 contents = self._run_cmd(['openssl', 'x509', '-inform', 'PEM', '-text',
97 '-in', self.cert_path])
98 description, begin_cert, cert_body = contents.rpartition('-----BEGIN '
99 'CERTIFICATE')
100 contents = ''.join([begin_cert, cert_body, description])
101 with open(self.reformatted_cert_path, 'w') as cert_file:
102 cert_file.write(contents)
104 def _remove_cert_from_cacerts(self):
105 self._adb_su_shell('mount', '-o', 'remount,rw', '/system')
106 self._adb_su_shell('rm', self.android_cacerts_path)
108 def _is_cert_installed(self):
109 return (self._adb_su_shell('ls', self.android_cacerts_path).strip() ==
110 self.android_cacerts_path)
112 def _generate_reformatted_cert_path(self):
113 # Determine OpenSSL version, string is of the form
114 # 'OpenSSL 0.9.8za 5 Jun 2014' .
115 openssl_version = self._run_cmd(['openssl', 'version']).split()
117 if len(openssl_version) < 2:
118 raise ValueError('Unexpected OpenSSL version string: ', openssl_version)
120 # subject_hash flag name changed as of OpenSSL version 1.0.0 .
121 is_old_openssl_version = openssl_version[1].startswith('0')
122 subject_hash_flag = (
123 '-subject_hash' if is_old_openssl_version else '-subject_hash_old')
125 output = self._run_cmd(['openssl', 'x509', '-inform', 'PEM',
126 subject_hash_flag, '-in', self.cert_path],
127 os.path.dirname(self.cert_path))
128 self.reformatted_cert_fname = output.partition('\n')[0].strip() + '.0'
129 self.reformatted_cert_path = os.path.join(os.path.dirname(self.cert_path),
130 self.reformatted_cert_fname)
131 self.android_cacerts_path = ('/system/etc/security/cacerts/%s' %
132 self.reformatted_cert_fname)
134 def remove_cert(self):
135 self._generate_reformatted_cert_path()
137 if self._is_cert_installed():
138 self._remove_cert_from_cacerts()
140 if self._is_cert_installed():
141 raise CertRemovalError('Cert Removal Failed')
143 def install_cert(self, overwrite_cert=False):
144 """Installs a certificate putting it in /system/etc/security/cacerts."""
145 self._generate_reformatted_cert_path()
147 if self._is_cert_installed():
148 if overwrite_cert:
149 self._remove_cert_from_cacerts()
150 else:
151 logging.info('cert is already installed')
152 return
154 self._format_hashed_cert()
155 self._adb('push', self.reformatted_cert_path, '/sdcard/')
156 self._remove(self.reformatted_cert_path)
157 self._adb_su_shell('mount', '-o', 'remount,rw', '/system')
158 self._adb_su_shell(
159 'cp', '/sdcard/%s' % self.reformatted_cert_fname,
160 '/system/etc/security/cacerts/%s' % self.reformatted_cert_fname)
161 self._adb_su_shell('chmod', '644', self.android_cacerts_path)
162 if not self._is_cert_installed():
163 raise CertInstallError('Cert Install Failed')
165 def install_cert_using_gui(self):
166 """Installs certificate on the device using adb commands."""
167 self.check_device()
168 # TODO(mruthven): Add a check to see if the certificate is already installed
169 # Install the certificate.
170 logging.info('Installing %s on %s', self.cert_path, self.device_id)
171 self._adb('push', self.cert_path, '/sdcard/')
173 # Start credential install intent.
174 self._adb('shell', 'am', 'start', '-W', '-a', 'android.credentials.INSTALL')
176 # Move to and click search button.
177 self._input_key(KEYCODE_TAB)
178 self._input_key(KEYCODE_TAB)
179 self._input_key(KEYCODE_ENTER)
181 # Search for certificate and click it.
182 # Search only works with lower case letters
183 self._input_text(self.file_name.lower())
184 self._input_key(KEYCODE_ENTER)
186 # These coordinates work for hammerhead devices.
187 self._adb('shell', 'input', 'tap', '300', '300')
189 # Name the certificate and click enter.
190 self._input_text(self.cert_name)
191 self._input_key(KEYCODE_TAB)
192 self._input_key(KEYCODE_TAB)
193 self._input_key(KEYCODE_TAB)
194 self._input_key(KEYCODE_ENTER)
196 # Remove the file.
197 self._adb('shell', 'rm', '/sdcard/' + self.file_name)
200 def parse_args():
201 """Parses command line arguments."""
202 parser = argparse.ArgumentParser(description='Install cert on device.')
203 parser.add_argument(
204 '-n', '--cert-name', default='dummycert', help='certificate name')
205 parser.add_argument(
206 '--overwrite', default=False, action='store_true',
207 help='Overwrite certificate file if it is already installed')
208 parser.add_argument(
209 '--remove', default=False, action='store_true',
210 help='Remove certificate file if it is installed')
211 parser.add_argument(
212 '--device-id', help='device serial number')
213 parser.add_argument(
214 'cert_path', help='Certificate file path')
215 return parser.parse_args()
218 def main():
219 args = parse_args()
220 cert_installer = AndroidCertInstaller(args.device_id, args.cert_name,
221 args.cert_path)
222 if args.remove:
223 cert_installer.remove_cert()
224 else:
225 cert_installer.install_cert(args.overwrite)
228 if __name__ == '__main__':
229 sys.exit(main())