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)) { 920967a9edfc29fe601c9242648b93448d710b7a97Geremy Condra Slog.i(TAG, "Not installing, new version is <= current version"); 93b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else if (!verifyPreviousHash(currentHash, altRequiredHash)) { 94beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, 95beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra "Current hash did not match required value"); 96b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig, 97b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra cert)) { 98beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, 99beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra "Signature did not verify"); 100b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } else { 101b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // install the new content 102b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Found new update, installing..."); 103b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra install(altContent, altVersion); 104b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.i(TAG, "Installation successful"); 105b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 106b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (Exception e) { 107b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Slog.e(TAG, "Could not update content!", e); 108beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra // keep the error message <= 100 chars 109beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra String errMsg = e.toString(); 110beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra if (errMsg.length() > 100) { 111beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra errMsg = errMsg.substring(0, 99); 112beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra } 113beb9d53971af42db178dfdf6bbcd28d3f823c5f8Geremy Condra EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, errMsg); 114b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 115b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 116b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra }.start(); 117b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 118b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 119b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private X509Certificate getCert(ContentResolver cr) { 120b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // get the cert from settings 121b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String cert = Settings.Secure.getString(cr, UPDATE_CERTIFICATE_KEY); 122b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // convert it into a real certificate 123b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 124b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT); 125b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra InputStream istream = new ByteArrayInputStream(derCert); 126b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra CertificateFactory cf = CertificateFactory.getInstance("X.509"); 127b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return (X509Certificate) cf.generateCertificate(istream); 128b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (CertificateException e) { 129c6fa237daeaae6107174a9b9d4f591ea8cd26d86Robert Greenwalt throw new IllegalStateException("Got malformed certificate from settings, ignoring"); 130b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 131b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 132b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 133b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getContentFromIntent(Intent i) { 134b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_CONTENT_PATH); 135b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 136b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required content path, ignoring."); 137b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 138b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue; 139b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 140b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 141b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private int getVersionFromIntent(Intent i) throws NumberFormatException { 142b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_VERSION_NUMBER); 143b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 144b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required version number, ignoring."); 145b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 146b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return Integer.parseInt(extraValue.trim()); 147b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 148b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 149b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getRequiredHashFromIntent(Intent i) { 150b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_REQUIRED_HASH); 151b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 152b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required previous hash, ignoring."); 153b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 154b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue.trim(); 155b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 156b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 157b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getSignatureFromIntent(Intent i) { 158b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String extraValue = i.getStringExtra(EXTRA_SIGNATURE); 159b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (extraValue == null) { 160b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new IllegalStateException("Missing required signature, ignoring."); 161b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 162b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return extraValue.trim(); 163b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 164b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 165b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private int getCurrentVersion() throws NumberFormatException { 166b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 167b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String strVersion = IoUtils.readFileAsString(updateVersion.getCanonicalPath()).trim(); 168b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return Integer.parseInt(strVersion); 169b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (IOException e) { 170c6fa237daeaae6107174a9b9d4f591ea8cd26d86Robert Greenwalt Slog.i(TAG, "Couldn't find current metadata, assuming first update"); 171b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return 0; 172b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 173b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 174b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 175b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getAltContent(Intent i) throws IOException { 176b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String contents = IoUtils.readFileAsString(getContentFromIntent(i)); 177b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return contents.trim(); 178b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 179b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 180b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private String getCurrentContent() { 181b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 182b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return IoUtils.readFileAsString(updateContent.getCanonicalPath()).trim(); 183b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (IOException e) { 184c6fa237daeaae6107174a9b9d4f591ea8cd26d86Robert Greenwalt Slog.i(TAG, "Failed to read current content, assuming first update!"); 185b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return null; 186b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 187b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 188b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 189b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private static String getCurrentHash(String content) { 190b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (content == null) { 191b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return "0"; 192b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 193b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 194b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra MessageDigest dgst = MessageDigest.getInstance("SHA512"); 195b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] encoded = content.getBytes(); 196b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra byte[] fingerprint = dgst.digest(encoded); 197b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return IntegralToString.bytesToHexString(fingerprint, false); 198b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } catch (NoSuchAlgorithmException e) { 199b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra throw new AssertionError(e); 200b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 201b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 202b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 203b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifyVersion(int current, int alternative) { 204b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return (current < alternative); 205b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 206b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 207b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifyPreviousHash(String current, String required) { 208b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // this is an optional value- if the required field is NONE then we ignore it 209b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (required.equals("NONE")) { 210b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return true; 211b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 212b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // otherwise, verify that we match correctly 213b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return current.equals(required); 214b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 215b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 216b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra private boolean verifySignature(String content, int version, String requiredPrevious, 217b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra String signature, X509Certificate cert) throws Exception { 218b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra Signature signer = Signature.getInstance("SHA512withRSA"); 219b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.initVerify(cert); 220b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(content.getBytes()); 221b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(Long.toString(version).getBytes()); 222b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra signer.update(requiredPrevious.getBytes()); 223b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); 224b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 225b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 226350bb15ac74e0abb68ce0e2b2b972dc3cef46affGeremy Condra private void writeUpdate(File dir, File file, String content) throws IOException { 227b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra FileOutputStream out = null; 228b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra File tmp = null; 229b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra try { 230b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // create the temporary file 231b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp = File.createTempFile("journal", "", dir); 232755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra // create the parents for the destination file 233755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra File parent = file.getParentFile(); 234755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra parent.mkdirs(); 235755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra // check that they were created correctly 236755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra if (!parent.exists()) { 237755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra throw new IOException("Failed to create directory " + parent.getCanonicalPath()); 238755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra } 239b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // mark tmp -rw-r--r-- 240b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp.setReadable(true, false); 241b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // write to it 242b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra out = new FileOutputStream(tmp); 243b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra out.write(content.getBytes()); 244b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // sync to disk 245755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra out.getFD().sync(); 246b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra // atomic rename 247755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra if (!tmp.renameTo(file)) { 248755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra throw new IOException("Failed to atomically rename " + file.getCanonicalPath()); 249755b87742319a9ff689df08cea0137732a8f0b2dGeremy Condra } 250b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } finally { 251b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra if (tmp != null) { 252b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra tmp.delete(); 253b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 254b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra IoUtils.closeQuietly(out); 255b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 256b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 257b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra 258350bb15ac74e0abb68ce0e2b2b972dc3cef46affGeremy Condra private void install(String content, int version) throws IOException { 259b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra writeUpdate(updateDir, updateContent, content); 260b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra writeUpdate(updateDir, updateVersion, Long.toString(version)); 261b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra } 262b631084613e12e1c6a0ae2ad9446e1284b650ccbGeremy Condra} 263