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