Skip to content

Commit 3073a0d

Browse files
committed
Ensure Chrome on Android doesn't validate our cert for Cert Transparency
1 parent fcd1421 commit 3073a0d

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

src/interceptors/android/adb-commands.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export async function injectSystemCertificate(
250250
echo "System cert successfully injected"
251251
`),
252252
injectionScriptPath,
253-
// Due to an Android bug - user mode is always duplicated to group & others. We set as read-only
253+
// Due to an Android bug, user mode is always duplicated to group & others. We set as read-only
254254
// to avoid making this writable by others before we run it as root in a moment.
255255
// More details: https://github.com/openstf/adbkit/issues/126
256256
0o444
@@ -261,6 +261,41 @@ export async function injectSystemCertificate(
261261
console.log(scriptOutput);
262262
}
263263

264+
export async function setChromeFlags(
265+
adbClient: adb.AdbClient,
266+
deviceId: string,
267+
rootCmd: string[],
268+
flags: string[]
269+
) {
270+
const flagsFileContent = `chrome ${flags.join(' ')}`;
271+
272+
const chromeFlagsLocations = [
273+
'chrome',
274+
'android-webview',
275+
'webview',
276+
'content-shell'
277+
].flatMap((variant) => [
278+
`/data/local/${variant}-command-line`,
279+
`/data/local/tmp/${variant}-command-line`,
280+
]);
281+
282+
await Promise.all(chromeFlagsLocations.map((flagsFilePath) =>
283+
pushFile(
284+
adbClient,
285+
deviceId,
286+
stringAsStream(flagsFileContent),
287+
flagsFilePath,
288+
// Due to an Android bug, user mode is always duplicated to group & others. We set as read-only
289+
// to avoid making this writable by others.
290+
// More details: https://github.com/openstf/adbkit/issues/126
291+
0o444 // Read-only for everybody
292+
)
293+
));
294+
295+
// Restart chrome, now that the flags have been changed:
296+
await run(adbClient, deviceId, rootCmd.concat('am', 'force-stop', 'com.android.chrome')).catch(() => {});
297+
}
298+
264299
export async function bringToFront(
265300
adbClient: adb.AdbClient,
266301
deviceId: string,

src/interceptors/android/android-adb-interceptor.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
injectSystemCertificate,
1717
stringAsStream,
1818
hasCertInstalled,
19-
bringToFront
19+
bringToFront,
20+
setChromeFlags
2021
} from './adb-commands';
2122
import { streamLatestApk, clearAllApks } from './fetch-apk';
2223
import { parseCert, getCertificateFingerprint, getCertificateSubjectHash } from '../../certificates';
@@ -185,24 +186,32 @@ export class AndroidAdbInterceptor implements Interceptor {
185186
const subjectHash = getCertificateSubjectHash(cert);
186187
const fingerprint = getCertificateFingerprint(cert);
187188

188-
if (await hasCertInstalled(this.adbClient, deviceId, subjectHash, fingerprint)) {
189+
if (!await hasCertInstalled(this.adbClient, deviceId, subjectHash, fingerprint)) {
190+
const certPath = `${ANDROID_TEMP}/${subjectHash}.0`;
191+
console.log(`Adding cert file as ${certPath}`);
192+
193+
await pushFile(
194+
this.adbClient,
195+
deviceId,
196+
stringAsStream(certContent.replace('\r\n', '\n')),
197+
certPath,
198+
0o444
199+
);
200+
201+
await injectSystemCertificate(this.adbClient, deviceId, rootCmd, certPath);
202+
console.log(`Cert injected`);
203+
} else {
189204
console.log("Cert already installed, nothing to do");
190-
return;
191205
}
192206

193-
const certPath = `${ANDROID_TEMP}/${subjectHash}.0`;
194-
console.log(`Adding cert file as ${certPath}`);
207+
const spkiFingerprint = generateSPKIFingerprint(certContent);
195208

196-
await pushFile(
197-
this.adbClient,
198-
deviceId,
199-
stringAsStream(certContent.replace('\r\n', '\n')),
200-
certPath,
201-
0o444
202-
);
203-
204-
await injectSystemCertificate(this.adbClient, deviceId, rootCmd, certPath);
205-
console.log(`Cert injected`);
209+
// Chrome requires system certificates to use certificate transparency, which we can't do. To work
210+
// around this, we need to explicitly trust our certificate in Chrome:
211+
await setChromeFlags(this.adbClient, deviceId, rootCmd, [
212+
`--ignore-certificate-errors-spki-list=${spkiFingerprint}`
213+
]);
214+
console.log('Android Chrome flags set');
206215
} catch (e) {
207216
reportError(e);
208217
}

0 commit comments

Comments
 (0)