168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// found in the LICENSE file.
468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)package org.chromium.chrome.browser;
668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import android.content.Context;
868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import android.os.AsyncTask;
968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import android.util.Log;
1068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
1168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.io.File;
1268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.io.FileInputStream;
1368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.io.FileOutputStream;
1468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.io.IOException;
1568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.security.GeneralSecurityException;
1668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.security.SecureRandom;
1768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.util.concurrent.Callable;
1868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.util.concurrent.ExecutionException;
1968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import java.util.concurrent.FutureTask;
2068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import javax.crypto.KeyGenerator;
2268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import javax.crypto.Mac;
2368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import javax.crypto.SecretKey;
2468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)import javax.crypto.spec.SecretKeySpec;
2568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)/**
2768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * Authenticate the source of Intents to launch web apps (see e.g. {@link #FullScreenActivity}).
2868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) *
2968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * Chrome does not keep a store of valid URLs for installed web apps (because it cannot know when
3068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * any have been uninstalled). Therefore, upon installation, it tells the Launcher a message
3168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * authentication code (MAC) along with the URL for the web app, and then Chrome can verify the MAC
3268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * when starting e.g. {@link #FullScreenActivity}. Chrome can thus distinguish between legitimate,
3368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * installed web apps and arbitrary other URLs.
3468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) */
3568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)public class WebappAuthenticator {
3668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static final String TAG = "WebappAuthenticator";
3768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static final String MAC_ALGORITHM_NAME = "HmacSHA256";
3868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static final String MAC_KEY_BASENAME = "webapp-authenticator";
3968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static final int MAC_KEY_BYTE_COUNT = 32;
4068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static final Object sLock = new Object();
4168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
4268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static FutureTask<SecretKey> sMacKeyGenerator;
4368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static SecretKey sKey = null;
4468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
4568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    /**
4668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @see #getMacForUrl
4768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     *
4868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @param url The URL to validate.
4968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @param mac The bytes of a previously-calculated MAC.
5068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     *
5168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @return true if the MAC is a valid MAC for the URL, false otherwise.
5268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     */
5368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    public static boolean isUrlValid(Context context, String url, byte[] mac) {
5468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        byte[] goodMac = getMacForUrl(context, url);
5568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (goodMac == null) {
5668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return false;
5768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
5868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        return constantTimeAreArraysEqual(goodMac, mac);
5968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
6068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
6168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    /**
6268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @see #isUrlValid
6368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     *
6468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @param url A URL for which to calculate a MAC.
6568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     *
6668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @return The bytes of a MAC for the URL, or null if a secure MAC was not available.
6768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     */
6868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    public static byte[] getMacForUrl(Context context, String url) {
6968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        Mac mac = getMac(context);
7068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (mac == null) {
7168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return null;
7268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
7368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        return mac.doFinal(url.getBytes());
7468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
7568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
7668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // TODO(palmer): Put this method, and as much of this class as possible, in a utility class.
7768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static boolean constantTimeAreArraysEqual(byte[] a, byte[] b) {
7868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (a.length != b.length) {
7968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return false;
8068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
8168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
8268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        int result = 0;
8368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        for (int i = 0; i < a.length; i++) {
8468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            result |= a[i] ^ b[i];
8568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
8668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        return result == 0;
8768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
8868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
8968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static SecretKey readKeyFromFile(
9068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Context context, String basename, String algorithmName) {
9168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        FileInputStream input = null;
9268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        File file = context.getFileStreamPath(basename);
9368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        try {
9468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (file.length() != MAC_KEY_BYTE_COUNT) {
9568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                Log.w(TAG, "Could not read key from '" + file + "': invalid file contents");
9668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return null;
9768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
9868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
9968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            byte[] keyBytes = new byte[MAC_KEY_BYTE_COUNT];
10068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            input = new FileInputStream(file);
10168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (MAC_KEY_BYTE_COUNT != input.read(keyBytes)) {
10268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return null;
10368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
10468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
10568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            try {
10668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return new SecretKeySpec(keyBytes, algorithmName);
10768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            } catch (IllegalArgumentException e) {
10868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return null;
10968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
11068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } catch (Exception e) {
11168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Log.w(TAG, "Could not read key from '" + file + "': " + e);
11268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return null;
11368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } finally {
11468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            try {
11568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                if (input != null) {
11668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    input.close();
11768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                }
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            } catch (IOException e) {
11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                Log.e(TAG, "Could not close key input stream '" + file + "': " + e);
12068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
12168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
12268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
12368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
12468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static boolean writeKeyToFile(Context context, String basename, SecretKey key) {
12568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        File file = context.getFileStreamPath(basename);
12668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        byte[] keyBytes = key.getEncoded();
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        FileOutputStream output = null;
12868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        if (MAC_KEY_BYTE_COUNT != keyBytes.length) {
12968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Log.e(TAG, "writeKeyToFile got key encoded bytes length " + keyBytes.length +
13068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                       "; expected " + MAC_KEY_BYTE_COUNT);
13168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return false;
13268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
13368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
13468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        try {
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            output = new FileOutputStream(file);
13668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            output.write(keyBytes);
13768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return true;
13868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } catch (Exception e) {
13968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Log.e(TAG, "Could not write key to '" + file + "': " + e);
14068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return false;
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        } finally {
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            try {
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                if (output != null) {
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    output.close();
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                }
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            } catch (IOException e) {
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                Log.e(TAG, "Could not close key output stream '" + file + "': " + e);
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            }
14968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
15068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
15168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
15268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static SecretKey getKey(Context context) {
15368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        synchronized (sLock) {
15468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (sKey == null) {
15568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                SecretKey key = readKeyFromFile(context, MAC_KEY_BASENAME, MAC_ALGORITHM_NAME);
15668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                if (key != null) {
15768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    sKey = key;
15868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    return sKey;
15968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                }
16068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
16168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                triggerMacKeyGeneration();
16268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                try {
16368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    sKey = sMacKeyGenerator.get();
16468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    sMacKeyGenerator = null;
16568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    if (!writeKeyToFile(context, MAC_KEY_BASENAME, sKey)) {
16668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                        sKey = null;
16768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                        return null;
16868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    }
16968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    return sKey;
17068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                } catch (InterruptedException e) {
17168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    throw new RuntimeException(e);
17268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                } catch (ExecutionException e) {
17368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    throw new RuntimeException(e);
17468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                }
17568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
17668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return sKey;
17768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
17868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
17968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
18068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    /**
18168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * Generates the authentication encryption key in a background thread (if necessary).
18268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     */
18368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static void triggerMacKeyGeneration() {
18468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        synchronized (sLock) {
18568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (sKey != null || sMacKeyGenerator != null) {
18668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return;
18768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
18868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
18968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            sMacKeyGenerator = new FutureTask<SecretKey>(new Callable<SecretKey>() {
19068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                @Override
19168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                public SecretKey call() throws Exception {
19268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    KeyGenerator generator = KeyGenerator.getInstance(MAC_ALGORITHM_NAME);
19368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
19468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
19568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // Versions of SecureRandom from Android <= 4.3 do not seed themselves as
19668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // securely as possible. This workaround should suffice until the fixed version
19768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // is deployed to all users. getRandomBytes, which reads from /dev/urandom,
19868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // which is as good as the platform can get.
19968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    //
20068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // TODO(palmer): Consider getting rid of this once the updated platform has
20168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // shipped to everyone. Alternately, leave this in as a defense against other
20268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    // bugs in SecureRandom.
20368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    byte[] seed = getRandomBytes(MAC_KEY_BYTE_COUNT);
20468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    if (seed == null) {
20568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                        return null;
20668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    }
20768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    random.setSeed(seed);
20868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    generator.init(MAC_KEY_BYTE_COUNT * 8, random);
20968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    return generator.generateKey();
21068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                }
21168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            });
21268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            AsyncTask.THREAD_POOL_EXECUTOR.execute(sMacKeyGenerator);
21368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
21468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
21568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
21668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static byte[] getRandomBytes(int count) {
21768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        FileInputStream fis = null;
21868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        try {
21968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            fis = new FileInputStream("/dev/urandom");
22068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            byte[] bytes = new byte[count];
22168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (bytes.length != fis.read(bytes)) {
22268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return null;
22368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
22468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return bytes;
22568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } catch (Throwable t) {
22668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            // This causes the ultimate caller, i.e. getMac, to fail.
22768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return null;
22868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } finally {
22968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            try {
23068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                if (fis != null) {
23168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                    fis.close();
23268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                }
23368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            } catch (IOException e) {
23468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                // Nothing we can do.
23568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
23668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
23768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
23868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
23968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    /**
24068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     * @return A Mac, or null if it is not possible to instantiate one.
24168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)     */
24268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    private static Mac getMac(Context context) {
24368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        try {
24468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            SecretKey key = getKey(context);
24568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            if (key == null) {
24668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                // getKey should have invoked triggerMacKeyGeneration, which should have set the
24768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                // random seed and generated a key from it. If not, there is a problem with the
24868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                // random number generator, and we must not claim that authentication can work.
24968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                return null;
25068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            }
25168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Mac mac = Mac.getInstance(MAC_ALGORITHM_NAME);
25268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            mac.init(key);
25368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return mac;
25468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        } catch (GeneralSecurityException e) {
25568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            Log.w(TAG, "Error in creating MAC instance", e);
25668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)            return null;
25768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        }
25868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
25968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
260