OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.content.browser.installedapp; | |
6 | |
7 import org.chromium.base.VisibleForTesting; | |
8 import org.chromium.content.browser.crypto.ByteArrayGenerator; | |
9 | |
10 import java.io.IOException; | |
11 import java.io.UnsupportedEncodingException; | |
12 import java.security.GeneralSecurityException; | |
13 import java.security.InvalidKeyException; | |
14 import java.security.Key; | |
15 import java.security.NoSuchAlgorithmException; | |
16 | |
17 import javax.crypto.Mac; | |
18 import javax.crypto.spec.SecretKeySpec; | |
19 | |
20 /** | |
21 * Helper class for retrieving a device-unique hash for an Android package name. | |
22 * | |
23 * This is used to counter a potential timing attack against the getInstalledRel atedApps API, by | |
24 * adding a pseudo-random time delay to the query. The delay is a hash of a glob ally unique | |
25 * identifier for the current browser session, and the package name, which means websites are unable | |
26 * to predict what each user's delay will be, nor compare between apps on a give n device. | |
27 * | |
28 * The salt is generated per browser session (not per query, page load, user or device) because it | |
29 * we want it to change "occasionally" -- not too frequently, but sometimes. Eac h time the salt | |
30 * changes, it gives the site another opportunity to collect data that could be averaged out to | |
31 * cancel out the random noise and find the true timing. So we don't want it cha nging too often. | |
32 * However, it does need to change periodically: because installing or uninstall ing the app creates | |
33 * a noticeable change to the timing of the operation, we need to occasionally c hange the salt to | |
34 * create plausible deniability (the attacker can't tell the difference between the salt changing | |
35 * and the app being installed/uninstalled). | |
36 */ | |
37 class PackageHash { | |
38 // Global salt string for the life of the browser process. A unique salt is generated for | |
39 // each run of the browser process that will be stable for its lifetime. | |
40 private static byte[] sSalt; | |
41 | |
42 /** | |
43 * Returns a SHA-256 hash of the package name, truncated to a 16-bit integer . | |
44 */ | |
45 public static short hashForPackage(String packageName) { | |
46 byte[] salt = getGlobalSalt(); | |
47 Mac hasher; | |
48 try { | |
49 hasher = Mac.getInstance("HmacSHA256"); | |
50 } catch (NoSuchAlgorithmException e) { | |
51 // Should never happen. | |
52 throw new RuntimeException(e); | |
53 } | |
54 | |
55 byte[] packageNameBytes; | |
56 try { | |
57 packageNameBytes = packageName.getBytes("UTF-8"); | |
58 } catch (UnsupportedEncodingException e) { | |
59 // Should never happen. | |
60 throw new RuntimeException(e); | |
61 } | |
62 | |
63 Key key = new SecretKeySpec(salt, "HmacSHA256"); | |
64 try { | |
65 hasher.init(key); | |
66 } catch (InvalidKeyException e) { | |
67 // Should never happen. | |
68 throw new RuntimeException(e); | |
69 } | |
70 byte[] digest = hasher.doFinal(packageNameBytes); | |
71 // Take just the first two bytes of the digest. | |
72 int hash = ((((int) digest[0]) & 0xff) << 8) | (((int) digest[1]) & 0xff ); | |
73 return (short) hash; | |
74 } | |
75 | |
76 /** | |
77 * Gets the global salt for the current browser session. | |
78 * | |
79 * If one does not exist, generates one using a PRNG and caches it, then ret urns it. | |
80 */ | |
81 private static byte[] getGlobalSalt() { | |
82 if (sSalt == null) { | |
83 try { | |
84 sSalt = new ByteArrayGenerator().getBytes(20); | |
85 } catch (IOException e) { | |
86 // TODO(mgiuca): How to handle this? DO NOT SUBMIT. | |
Matt Giuca
2017/04/11 08:34:08
palmer: Do you have any suggestion of what to do h
Matt Giuca
2017/04/12 05:38:02
Done (discussed in comment thread).
| |
87 throw new RuntimeException(e); | |
88 } catch (GeneralSecurityException e) { | |
89 // TODO(mgiuca): How to handle this? DO NOT SUBMIT. | |
90 throw new RuntimeException(e); | |
91 } | |
92 } | |
93 | |
94 return sSalt; | |
95 } | |
96 | |
97 @VisibleForTesting | |
98 public static void setGlobalSaltForTesting(byte[] salt) { | |
99 sSalt = salt; | |
100 } | |
101 } | |
OLD | NEW |