14ad1aa43a48567659193a298fad74f55e00b3dd9Ben Murdoch/* 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (C) 2012 The Android Open Source Project 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Licensed under the Apache License, Version 2.0 (the "License"); 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * you may not use this file except in compliance with the License. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * You may obtain a copy of the License at 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * http://www.apache.org/licenses/LICENSE-2.0 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Unless required by applicable law or agreed to in writing, software 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * distributed under the License is distributed on an "AS IS" BASIS, 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * See the License for the specific language governing permissions and 147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * limitations under the License. 15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochpackage com.android.server.updates; 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.BroadcastReceiver; 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.ContentResolver; 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.Context; 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.Intent; 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.net.Uri; 24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.provider.Settings; 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.util.Base64; 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.util.EventLog; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import android.util.Slog; 287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import com.android.server.EventLogTags; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport java.io.ByteArrayInputStream; 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.io.File; 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.io.FileOutputStream; 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.io.InputStream; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.io.IOException; 367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport java.security.cert.CertificateException; 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.security.cert.CertificateFactory; 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.security.cert.X509Certificate; 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.security.MessageDigest; 407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport java.security.NoSuchAlgorithmException; 41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.security.Signature; 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import libcore.io.IoUtils; 447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport libcore.io.Streams; 45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)public class ConfigUpdateInstallReceiver extends BroadcastReceiver { 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String TAG = "ConfigUpdateInstallReceiver"; 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH"; 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH"; 52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String EXTRA_SIGNATURE = "SIGNATURE"; 53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String EXTRA_VERSION_NUMBER = "VERSION"; 54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate"; 56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) protected final File updateDir; 58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) protected final File updateContent; 59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) protected final File updateVersion; 60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath, 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) String updateMetadataPath, String updateVersionPath) { 637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch this.updateDir = new File(updateDir); 64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) this.updateContent = new File(updateDir, updateContentPath); 65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) File updateMetadataDir = new File(updateDir, updateMetadataPath); 66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) this.updateVersion = new File(updateMetadataDir, updateVersionPath); 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void onReceive(final Context context, final Intent intent) { 717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch new Thread() { 72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void run() { 74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try { 75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the certificate from Settings.Secure 76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) X509Certificate cert = getCert(context.getContentResolver()); 77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the content path from the extras 78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) byte[] altContent = getAltContent(context, intent); 79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the version from the extras 80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int altVersion = getVersionFromIntent(intent); 81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the previous value from the extras 82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String altRequiredHash = getRequiredHashFromIntent(intent); 83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the signature from the extras 84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String altSig = getSignatureFromIntent(intent); 85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the version currently being used 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int currentVersion = getCurrentVersion(); 87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the hash of the currently used value 88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String currentHash = getCurrentHash(getCurrentContent()); 89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!verifyVersion(currentVersion, altVersion)) { 90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.i(TAG, "Not installing, new version is <= current version"); 91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else if (!verifyPreviousHash(currentHash, altRequiredHash)) { 92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, 93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "Current hash did not match required value"); 94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig, 95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cert)) { 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "Signature did not verify"); 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // install the new content 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.i(TAG, "Found new update, installing..."); 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) install(altContent, altVersion); 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.i(TAG, "Installation successful"); 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) postInstall(context, intent); 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } catch (Exception e) { 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.e(TAG, "Could not update content!", e); 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // keep the error message <= 100 chars 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String errMsg = e.toString(); 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (errMsg.length() > 100) { 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) errMsg = errMsg.substring(0, 99); 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, errMsg); 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }.start(); 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private X509Certificate getCert(ContentResolver cr) { 119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // get the cert from settings 120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String cert = Settings.Secure.getString(cr, UPDATE_CERTIFICATE_KEY); 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // convert it into a real certificate 122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try { 123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT); 124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) InputStream istream = new ByteArrayInputStream(derCert); 125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) CertificateFactory cf = CertificateFactory.getInstance("X.509"); 126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return (X509Certificate) cf.generateCertificate(istream); 127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } catch (CertificateException e) { 128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException("Got malformed certificate from settings, ignoring"); 129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private Uri getContentFromIntent(Intent i) { 133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Uri data = i.getData(); 134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (data == null) { 135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException("Missing required content path, ignoring."); 136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return data; 138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private int getVersionFromIntent(Intent i) throws NumberFormatException { 141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String extraValue = i.getStringExtra(EXTRA_VERSION_NUMBER); 142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (extraValue == null) { 143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException("Missing required version number, ignoring."); 144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Integer.parseInt(extraValue.trim()); 146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private String getRequiredHashFromIntent(Intent i) { 149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String extraValue = i.getStringExtra(EXTRA_REQUIRED_HASH); 150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (extraValue == null) { 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException("Missing required previous hash, ignoring."); 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return extraValue.trim(); 154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private String getSignatureFromIntent(Intent i) { 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String extraValue = i.getStringExtra(EXTRA_SIGNATURE); 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (extraValue == null) { 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException("Missing required signature, ignoring."); 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return extraValue.trim(); 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private int getCurrentVersion() throws NumberFormatException { 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try { 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) String strVersion = IoUtils.readFileAsString(updateVersion.getCanonicalPath()).trim(); 167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Integer.parseInt(strVersion); 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } catch (IOException e) { 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.i(TAG, "Couldn't find current metadata, assuming first update"); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 1717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch private byte[] getAltContent(Context c, Intent i) throws IOException { 175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Uri content = getContentFromIntent(i); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InputStream is = c.getContentResolver().openInputStream(content); 177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try { 178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Streams.readFullyNoClose(is); 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } finally { 180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) is.close(); 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private byte[] getCurrentContent() { 1857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch try { 186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return IoUtils.readFileAsByteArray(updateContent.getCanonicalPath()); 187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } catch (IOException e) { 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Slog.i(TAG, "Failed to read current content, assuming first update!"); 189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return null; 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private static String getCurrentHash(byte[] content) { 1947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (content == null) { 195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return "0"; 196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try { 1987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch MessageDigest dgst = MessageDigest.getInstance("SHA512"); 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) byte[] fingerprint = dgst.digest(content); 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return IntegralToString.bytesToHexString(fingerprint, false); 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } catch (NoSuchAlgorithmException e) { 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new AssertionError(e); 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private boolean verifyVersion(int current, int alternative) { 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (current < alternative); 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch private boolean verifyPreviousHash(String current, String required) { 211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // this is an optional value- if the required field is NONE then we ignore it 212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (required.equals("NONE")) { 213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return true; 214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // otherwise, verify that we match correctly 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return current.equals(required); 217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private boolean verifySignature(byte[] content, int version, String requiredPrevious, 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) String signature, X509Certificate cert) throws Exception { 2217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch Signature signer = Signature.getInstance("SHA512withRSA"); 222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) signer.initVerify(cert); 223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) signer.update(content); 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signer.update(Long.toString(version).getBytes()); 2257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch signer.update(requiredPrevious.getBytes()); 226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) protected void writeUpdate(File dir, File file, byte[] content) throws IOException { 230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) FileOutputStream out = null; 231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) File tmp = null; 232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try { 233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // create the parents for the destination file 234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) File parent = file.getParentFile(); 235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) parent.mkdirs(); 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // check that they were created correctly 2377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (!parent.exists()) { 238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IOException("Failed to create directory " + parent.getCanonicalPath()); 239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // create the temporary file 2417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch tmp = File.createTempFile("journal", "", dir); 242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // mark tmp -rw-r--r-- 243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) tmp.setReadable(true, false); 244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // write to it 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out = new FileOutputStream(tmp); 2467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch out.write(content); 247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // sync to disk 248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out.getFD().sync(); 249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // atomic rename 250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!tmp.renameTo(file)) { 251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IOException("Failed to atomically rename " + file.getCanonicalPath()); 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } finally { 254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (tmp != null) { 255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) tmp.delete(); 256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) IoUtils.closeQuietly(out); 258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) protected void install(byte[] content, int version) throws IOException { 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) writeUpdate(updateDir, updateContent, content); 2637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes()); 264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected void postInstall(Context context, Intent intent) { 2677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)