ConfigUpdateInstallReceiver.java revision 7c65e39964a1aa8fffbd940c5ee9e77691aa9656
1b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra/* 2b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * Copyright (C) 2012 The Android Open Source Project 3b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * 4b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 5b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * you may not use this file except in compliance with the License. 6b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * You may obtain a copy of the License at 7b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * 8b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * http://www.apache.org/licenses/LICENSE-2.0 9b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * 10b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * Unless required by applicable law or agreed to in writing, software 11b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 12b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * See the License for the specific language governing permissions and 14b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra * limitations under the License. 15b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra */ 16b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 17b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condrapackage com.android.server.updates; 18b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 19b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.content.BroadcastReceiver; 20b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.content.ContentResolver; 21b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.content.Context; 22b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.content.Intent; 23b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.provider.Settings; 24b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.os.FileUtils; 25b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.util.Base64; 267c65e39964a1aa8fffbd940c5ee9e77691aa9656Geremy Condraimport android.util.EventLog; 27b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport android.util.Slog; 28b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 297c65e39964a1aa8fffbd940c5ee9e77691aa9656Geremy Condraimport com.android.server.EventLogTags; 307c65e39964a1aa8fffbd940c5ee9e77691aa9656Geremy Condra 31b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.ByteArrayInputStream; 32b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.File; 33b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.FileNotFoundException; 34b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.FileOutputStream; 35b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.InputStream; 36b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.io.IOException; 37b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.cert.Certificate; 38b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.cert.CertificateException; 39b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.cert.CertificateFactory; 40b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.cert.X509Certificate; 41b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.MessageDigest; 42b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.NoSuchAlgorithmException; 43b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.Signature; 44b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport java.security.SignatureException; 45b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 46b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condraimport libcore.io.IoUtils; 47b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 48b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condrapublic class ConfigUpdateInstallReceiver extends BroadcastReceiver { 49b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 50b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String TAG = "ConfigUpdateInstallReceiver"; 51b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 52b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH"; 53b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH"; 54b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String EXTRA_SIGNATURE = "SIGNATURE"; 55b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String EXTRA_VERSION_NUMBER = "VERSION"; 56b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 57b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate"; 58b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 59b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private final File updateDir; 60b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private final File updateContent; 61b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private final File updateVersion; 62b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 63b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath, 64b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String updateMetadataPath, String updateVersionPath) { 65b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra this.updateDir = new File(updateDir); 66b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra this.updateContent = new File(updateDir, updateContentPath); 67b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra File updateMetadataDir = new File(updateDir, updateMetadataPath); 68b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra this.updateVersion = new File(updateMetadataDir, updateVersionPath); 69b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 70b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 71b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra @Override 72b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra public void onReceive(final Context context, final Intent intent) { 73b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra new Thread() { 74b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra @Override 75b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra public void run() { 76b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 77b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the certificate from Settings.Secure 78b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra X509Certificate cert = getCert(context.getContentResolver()); 79b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the content path from the extras 80b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String altContent = getAltContent(intent); 81b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the version from the extras 82b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra int altVersion = getVersionFromIntent(intent); 83b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the previous value from the extras 84b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String altRequiredHash = getRequiredHashFromIntent(intent); 85b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the signature from the extras 86b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String altSig = getSignatureFromIntent(intent); 87b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the version currently being used 88b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra int currentVersion = getCurrentVersion(); 89b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the hash of the currently used value 90b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String currentHash = getCurrentHash(getCurrentContent()); 91b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (!verifyVersion(currentVersion, altVersion)) { 92b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "New version is not greater than current version, aborting!"); 93b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else if (!verifyPreviousHash(currentHash, altRequiredHash)) { 94b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "Current hash did not match required value, aborting!"); 95b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig, 96b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra cert)) { 97b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "Signature did not verify, aborting!"); 98b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else { 99b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // install the new content 100b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Found new update, installing..."); 101b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra install(altContent, altVersion); 102b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Installation successful"); 103b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 104b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (Exception e) { 105b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "Could not update content!", e); 1067c65e39964a1aa8fffbd940c5ee9e77691aa9656Geremy Condra EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, 1077c65e39964a1aa8fffbd940c5ee9e77691aa9656Geremy Condra updateDir.toString()); 108b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 109b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 110b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra }.start(); 111b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 112b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 113b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private X509Certificate getCert(ContentResolver cr) { 114b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the cert from settings 115b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String cert = Settings.Secure.getString(cr, UPDATE_CERTIFICATE_KEY); 116b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // convert it into a real certificate 117b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 118b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT); 119b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra InputStream istream = new ByteArrayInputStream(derCert); 120b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra CertificateFactory cf = CertificateFactory.getInstance("X.509"); 121b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return (X509Certificate) cf.generateCertificate(istream); 122b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (CertificateException e) { 123b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Got malformed certificate from settings, ignoring", e); 124b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 125b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 126b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 127b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getContentFromIntent(Intent i) { 128b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_CONTENT_PATH); 129b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 130b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required content path, ignoring."); 131b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 132b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue; 133b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 134b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 135b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private int getVersionFromIntent(Intent i) throws NumberFormatException { 136b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_VERSION_NUMBER); 137b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 138b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required version number, ignoring."); 139b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 140b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return Integer.parseInt(extraValue.trim()); 141b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 142b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 143b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getRequiredHashFromIntent(Intent i) { 144b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_REQUIRED_HASH); 145b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 146b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required previous hash, ignoring."); 147b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 148b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue.trim(); 149b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 150b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 151b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getSignatureFromIntent(Intent i) { 152b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_SIGNATURE); 153b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 154b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required signature, ignoring."); 155b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 156b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue.trim(); 157b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 158b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 159b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private int getCurrentVersion() throws NumberFormatException { 160b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 161b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String strVersion = IoUtils.readFileAsString(updateVersion.getCanonicalPath()).trim(); 162b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return Integer.parseInt(strVersion); 163b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (IOException e) { 164b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Couldn't find current metadata, assuming first update", e); 165b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return 0; 166b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 167b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 168b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 169b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getAltContent(Intent i) throws IOException { 170b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String contents = IoUtils.readFileAsString(getContentFromIntent(i)); 171b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return contents.trim(); 172b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 173b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 174b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getCurrentContent() { 175b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 176b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return IoUtils.readFileAsString(updateContent.getCanonicalPath()).trim(); 177b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (IOException e) { 178b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Failed to read current content, assuming first update!", e); 179b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return null; 180b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 181b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 182b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 183b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static String getCurrentHash(String content) { 184b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (content == null) { 185b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return "0"; 186b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 187b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 188b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra MessageDigest dgst = MessageDigest.getInstance("SHA512"); 189b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] encoded = content.getBytes(); 190b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] fingerprint = dgst.digest(encoded); 191b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return IntegralToString.bytesToHexString(fingerprint, false); 192b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (NoSuchAlgorithmException e) { 193b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new AssertionError(e); 194b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 195b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 196b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 197b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifyVersion(int current, int alternative) { 198b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return (current < alternative); 199b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 200b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 201b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifyPreviousHash(String current, String required) { 202b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // this is an optional value- if the required field is NONE then we ignore it 203b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (required.equals("NONE")) { 204b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return true; 205b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 206b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // otherwise, verify that we match correctly 207b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return current.equals(required); 208b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 209b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 210b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifySignature(String content, int version, String requiredPrevious, 211b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String signature, X509Certificate cert) throws Exception { 212b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Signature signer = Signature.getInstance("SHA512withRSA"); 213b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.initVerify(cert); 214b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(content.getBytes()); 215b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(Long.toString(version).getBytes()); 216b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(requiredPrevious.getBytes()); 217b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); 218b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 219b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 220b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private void writeUpdate(File dir, File file, String content) { 221b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra FileOutputStream out = null; 222b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra File tmp = null; 223b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 224b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // create the temporary file 225b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp = File.createTempFile("journal", "", dir); 226755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra // create the parents for the destination file 227755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra File parent = file.getParentFile(); 228755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra parent.mkdirs(); 229755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra // check that they were created correctly 230755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra if (!parent.exists()) { 231755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra throw new IOException("Failed to create directory " + parent.getCanonicalPath()); 232755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra } 233b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // mark tmp -rw-r--r-- 234b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp.setReadable(true, false); 235b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // write to it 236b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra out = new FileOutputStream(tmp); 237b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra out.write(content.getBytes()); 238b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // sync to disk 239755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra out.getFD().sync(); 240b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // atomic rename 241755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra if (!tmp.renameTo(file)) { 242755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra throw new IOException("Failed to atomically rename " + file.getCanonicalPath()); 243755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra } 244b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (IOException e) { 245b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "Failed to write update", e); 246b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } finally { 247b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (tmp != null) { 248b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp.delete(); 249b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 250b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra IoUtils.closeQuietly(out); 251b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 252b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 253b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 254b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private void install(String content, int version) { 255b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra writeUpdate(updateDir, updateContent, content); 256b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra writeUpdate(updateDir, updateVersion, Long.toString(version)); 257b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 258b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra} 259