diff options
Diffstat (limited to 'core/java')
8 files changed, 575 insertions, 9 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0510de1..191a696 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -24,6 +24,7 @@ import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; @@ -973,10 +974,10 @@ final class ApplicationPackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, - ManifestDigest manifestDigest) { + ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { try { mPM.installPackageWithVerification(packageURI, observer, flags, installerPackageName, - verificationURI, manifestDigest); + verificationURI, manifestDigest, encryptionParams); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/content/pm/ContainerEncryptionParams.aidl b/core/java/android/content/pm/ContainerEncryptionParams.aidl new file mode 100644 index 0000000..c13d946 --- /dev/null +++ b/core/java/android/content/pm/ContainerEncryptionParams.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable ContainerEncryptionParams; diff --git a/core/java/android/content/pm/ContainerEncryptionParams.java b/core/java/android/content/pm/ContainerEncryptionParams.java new file mode 100644 index 0000000..5b1440d --- /dev/null +++ b/core/java/android/content/pm/ContainerEncryptionParams.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Slog; + +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * Represents encryption parameters used to read a container. + * + * @hide + */ +public class ContainerEncryptionParams implements Parcelable { + protected static final String TAG = "ContainerEncryptionParams"; + + /** What we print out first when toString() is called. */ + private static final String TO_STRING_PREFIX = "ContainerEncryptionParams{"; + + /** + * Parameter type for parceling that indicates the next parameters are + * IvParameters. + */ + private static final int ENC_PARAMS_IV_PARAMETERS = 1; + + /** Parameter type for paceling that indicates there are no MAC parameters. */ + private static final int MAC_PARAMS_NONE = 1; + + /** The encryption algorithm used. */ + private final String mEncryptionAlgorithm; + + /** The parameter spec to be used for encryption. */ + private final IvParameterSpec mEncryptionSpec; + + /** Secret key to be used for decryption. */ + private final SecretKey mEncryptionKey; + + /** Algorithm name for the MAC to be used. */ + private final String mMacAlgorithm; + + /** The parameter spec to be used for the MAC tag authentication. */ + private final AlgorithmParameterSpec mMacSpec; + + /** Secret key to be used for MAC tag authentication. */ + private final SecretKey mMacKey; + + /** MAC tag authenticating the data in the container. */ + private final byte[] mMacTag; + + /** Offset into file where authenticated (e.g., MAC protected) data begins. */ + private final int mAuthenticatedDataStart; + + /** Offset into file where encrypted data begins. */ + private final int mEncryptedDataStart; + + /** + * Offset into file for the end of encrypted data (and, by extension, + * authenticated data) in file. + */ + private final int mDataEnd; + + public ContainerEncryptionParams(String encryptionAlgorithm, + AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey) + throws InvalidAlgorithmParameterException { + this(encryptionAlgorithm, encryptionSpec, encryptionKey, null, null, null, null, -1, -1, + -1); + } + + /** + * Creates container encryption specifications for installing from encrypted + * containers. + * + * @param encryptionAlgorithm encryption algorithm to use; format matches + * JCE + * @param encryptionSpec algorithm parameter specification + * @param encryptionKey key used for decryption + * @param macAlgorithm MAC algorithm to use; format matches JCE + * @param macSpec algorithm parameters specification, may be {@code null} + * @param macKey key used for authentication (i.e., for the MAC tag) + * @param authenticatedDataStart offset of start of authenticated data in + * stream + * @param encryptedDataStart offset of start of encrypted data in stream + * @param dataEnd offset of the end of both the authenticated and encrypted + * data + * @throws InvalidAlgorithmParameterException + */ + public ContainerEncryptionParams(String encryptionAlgorithm, + AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey, String macAlgorithm, + AlgorithmParameterSpec macSpec, SecretKey macKey, byte[] macTag, + int authenticatedDataStart, int encryptedDataStart, int dataEnd) + throws InvalidAlgorithmParameterException { + if (TextUtils.isEmpty(encryptionAlgorithm)) { + throw new NullPointerException("algorithm == null"); + } else if (encryptionSpec == null) { + throw new NullPointerException("encryptionSpec == null"); + } else if (encryptionKey == null) { + throw new NullPointerException("encryptionKey == null"); + } + + if (!TextUtils.isEmpty(macAlgorithm)) { + if (macKey == null) { + throw new NullPointerException("macKey == null"); + } + } + + if (!(encryptionSpec instanceof IvParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Unknown parameter spec class; must be IvParameters"); + } + + mEncryptionAlgorithm = encryptionAlgorithm; + mEncryptionSpec = (IvParameterSpec) encryptionSpec; + mEncryptionKey = encryptionKey; + + mMacAlgorithm = macAlgorithm; + mMacSpec = macSpec; + mMacKey = macKey; + mMacTag = macTag; + + mAuthenticatedDataStart = authenticatedDataStart; + mEncryptedDataStart = encryptedDataStart; + mDataEnd = dataEnd; + } + + public String getEncryptionAlgorithm() { + return mEncryptionAlgorithm; + } + + public AlgorithmParameterSpec getEncryptionSpec() { + return mEncryptionSpec; + } + + public SecretKey getEncryptionKey() { + return mEncryptionKey; + } + + public String getMacAlgorithm() { + return mMacAlgorithm; + } + + public AlgorithmParameterSpec getMacSpec() { + return mMacSpec; + } + + public SecretKey getMacKey() { + return mMacKey; + } + + public byte[] getMacTag() { + return mMacTag; + } + + public int getAuthenticatedDataStart() { + return mAuthenticatedDataStart; + } + + public int getEncryptedDataStart() { + return mEncryptedDataStart; + } + + public int getDataEnd() { + return mDataEnd; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ContainerEncryptionParams)) { + return false; + } + + final ContainerEncryptionParams other = (ContainerEncryptionParams) o; + + // Primitive comparison + if ((mAuthenticatedDataStart != other.mAuthenticatedDataStart) + || (mEncryptedDataStart != other.mEncryptedDataStart) + || (mDataEnd != other.mDataEnd)) { + return false; + } + + // String comparison + if (!mEncryptionAlgorithm.equals(other.mEncryptionAlgorithm) + || !mMacAlgorithm.equals(other.mMacAlgorithm)) { + return false; + } + + // Object comparison + if (!isSecretKeyEqual(mEncryptionKey, other.mEncryptionKey) + || !isSecretKeyEqual(mMacKey, other.mMacKey)) { + return false; + } + + if (!Arrays.equals(mEncryptionSpec.getIV(), other.mEncryptionSpec.getIV()) + || !Arrays.equals(mMacTag, other.mMacTag) || (mMacSpec != other.mMacSpec)) { + return false; + } + + return true; + } + + private static final boolean isSecretKeyEqual(SecretKey key1, SecretKey key2) { + final String keyFormat = key1.getFormat(); + final String otherKeyFormat = key2.getFormat(); + + if (keyFormat == null) { + if (keyFormat != otherKeyFormat) { + return false; + } + + if (key1.getEncoded() != key2.getEncoded()) { + return false; + } + } else { + if (!keyFormat.equals(key2.getFormat())) { + return false; + } + + if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + int hash = 3; + + hash += 5 * mEncryptionAlgorithm.hashCode(); + hash += 7 * Arrays.hashCode(mEncryptionSpec.getIV()); + hash += 11 * mEncryptionKey.hashCode(); + hash += 13 * mMacAlgorithm.hashCode(); + hash += 17 * mMacKey.hashCode(); + hash += 19 * Arrays.hashCode(mMacTag); + hash += 23 * mAuthenticatedDataStart; + hash += 29 * mEncryptedDataStart; + hash += 31 * mDataEnd; + + return hash; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX); + + sb.append("mEncryptionAlgorithm=\""); + sb.append(mEncryptionAlgorithm); + sb.append("\","); + sb.append("mEncryptionSpec="); + sb.append(mEncryptionSpec.toString()); + sb.append("mEncryptionKey="); + sb.append(mEncryptionKey.toString()); + + sb.append("mMacAlgorithm=\""); + sb.append(mMacAlgorithm); + sb.append("\","); + sb.append("mMacSpec="); + sb.append(mMacSpec.toString()); + sb.append("mMacKey="); + sb.append(mMacKey.toString()); + + sb.append(",mAuthenticatedDataStart="); + sb.append(mAuthenticatedDataStart); + sb.append(",mEncryptedDataStart="); + sb.append(mEncryptedDataStart); + sb.append(",mDataEnd="); + sb.append(mDataEnd); + sb.append('}'); + + return sb.toString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mEncryptionAlgorithm); + dest.writeInt(ENC_PARAMS_IV_PARAMETERS); + dest.writeByteArray(mEncryptionSpec.getIV()); + dest.writeSerializable(mEncryptionKey); + + dest.writeString(mMacAlgorithm); + dest.writeInt(MAC_PARAMS_NONE); + dest.writeByteArray(new byte[0]); + dest.writeSerializable(mMacKey); + + dest.writeByteArray(mMacTag); + + dest.writeInt(mAuthenticatedDataStart); + dest.writeInt(mEncryptedDataStart); + dest.writeInt(mDataEnd); + } + + private ContainerEncryptionParams(Parcel source) throws InvalidAlgorithmParameterException { + mEncryptionAlgorithm = source.readString(); + final int encParamType = source.readInt(); + final byte[] encParamsEncoded = source.createByteArray(); + mEncryptionKey = (SecretKey) source.readSerializable(); + + mMacAlgorithm = source.readString(); + final int macParamType = source.readInt(); + source.createByteArray(); // byte[] macParamsEncoded + mMacKey = (SecretKey) source.readSerializable(); + + mMacTag = source.createByteArray(); + + mAuthenticatedDataStart = source.readInt(); + mEncryptedDataStart = source.readInt(); + mDataEnd = source.readInt(); + + switch (encParamType) { + case ENC_PARAMS_IV_PARAMETERS: + mEncryptionSpec = new IvParameterSpec(encParamsEncoded); + break; + default: + throw new InvalidAlgorithmParameterException("Unknown parameter type " + + encParamType); + } + + switch (macParamType) { + case MAC_PARAMS_NONE: + mMacSpec = null; + break; + default: + throw new InvalidAlgorithmParameterException("Unknown parameter type " + + macParamType); + } + + if (mEncryptionKey == null) { + throw new NullPointerException("encryptionKey == null"); + } + } + + public static final Parcelable.Creator<ContainerEncryptionParams> CREATOR = + new Parcelable.Creator<ContainerEncryptionParams>() { + public ContainerEncryptionParams createFromParcel(Parcel source) { + try { + return new ContainerEncryptionParams(source); + } catch (InvalidAlgorithmParameterException e) { + Slog.e(TAG, "Invalid algorithm parameters specified", e); + return null; + } + } + + public ContainerEncryptionParams[] newArray(int size) { + return new ContainerEncryptionParams[size]; + } + }; +}
\ No newline at end of file diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 9b8454a..70c0c48 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageDeleteObserver; @@ -362,7 +363,7 @@ interface IPackageManager { void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer, int flags, in String installerPackageName, in Uri verificationURI, - in ManifestDigest manifestDigest); + in ManifestDigest manifestDigest, in ContainerEncryptionParams encryptionParams); void verifyPendingInstall(int id, int verificationCode); diff --git a/core/java/android/content/pm/LimitedLengthInputStream.java b/core/java/android/content/pm/LimitedLengthInputStream.java new file mode 100644 index 0000000..25a490f --- /dev/null +++ b/core/java/android/content/pm/LimitedLengthInputStream.java @@ -0,0 +1,82 @@ +package android.content.pm; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A class that limits the amount of data that is read from an InputStream. When + * the specified length is reached, the stream returns an EOF even if the + * underlying stream still has more data. + * + * @hide + */ +public class LimitedLengthInputStream extends FilterInputStream { + /** + * The end of the stream where we don't want to allow more data to be read. + */ + private final int mEnd; + + /** + * Current offset in the stream. + */ + private int mOffset; + + /** + * @param in underlying stream to wrap + * @param offset offset into stream where data starts + * @param length length of data at offset + * @throws IOException if an error occured with the underlying stream + */ + public LimitedLengthInputStream(InputStream in, int offset, int length) throws IOException { + super(in); + + if (in == null) { + throw new IOException("in == null"); + } + + if (offset < 0) { + throw new IOException("offset == " + offset); + } + + if (length < 0) { + throw new IOException("length must be non-negative; is " + length); + } + + mEnd = offset + length; + + skip(offset); + mOffset = offset; + } + + @Override + public synchronized int read() throws IOException { + if (mOffset >= mEnd) { + return -1; + } + + mOffset++; + return super.read(); + } + + @Override + public int read(byte[] buffer, int offset, int byteCount) throws IOException { + if (mOffset >= mEnd) { + return -1; + } + + if (mOffset + byteCount > mEnd) { + byteCount = mEnd - mOffset; + } + + final int numRead = super.read(buffer, offset, byteCount); + mOffset += numRead; + + return numRead; + } + + @Override + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } +} diff --git a/core/java/android/content/pm/MacAuthenticatedInputStream.java b/core/java/android/content/pm/MacAuthenticatedInputStream.java new file mode 100644 index 0000000..11f4b94 --- /dev/null +++ b/core/java/android/content/pm/MacAuthenticatedInputStream.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.crypto.Mac; + +/** + * An input stream filter that applies a MAC to the data passing through it. At + * the end of the data that should be authenticated, the tag can be calculated. + * After that, the stream should not be used. + * + * @hide + */ +public class MacAuthenticatedInputStream extends FilterInputStream { + private final Mac mMac; + + public MacAuthenticatedInputStream(InputStream in, Mac mac) { + super(in); + + mMac = mac; + } + + public boolean isTagEqual(byte[] tag) { + final byte[] actualTag = mMac.doFinal(); + + if (tag == null || actualTag == null || tag.length != actualTag.length) { + return false; + } + + /* + * Attempt to prevent timing attacks by doing the same amount of work + * whether the first byte matches or not. Do not change this to a loop + * that exits early when a byte does not match. + */ + int value = 0; + for (int i = 0; i < tag.length; i++) { + value |= tag[i] ^ actualTag[i]; + } + + return value == 0; + } + + @Override + public int read() throws IOException { + final int b = super.read(); + if (b >= 0) { + mMac.update((byte) b); + } + return b; + } + + @Override + public int read(byte[] buffer, int offset, int count) throws IOException { + int numRead = super.read(buffer, offset, count); + if (numRead > 0) { + mMac.update(buffer, offset, numRead); + } + return numRead; + } +} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c3ce1cf..a48924e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -28,7 +28,6 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Build; import android.os.Environment; import android.util.AndroidException; import android.util.DisplayMetrics; @@ -2199,12 +2198,19 @@ public abstract class PackageManager { * is performing the installation. This identifies which market * the package came from. * @param verificationURI The location of the supplementary verification - * file. This can be a 'file:' or a 'content:' URI. + * file. This can be a 'file:' or a 'content:' URI. May be + * {@code null}. + * @param manifestDigest an object that holds the digest of the package + * which can be used to verify ownership. May be {@code null}. + * @param encryptionParams if the package to be installed is encrypted, + * these parameters describing the encryption and authentication + * used. May be {@code null}. * @hide */ public abstract void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, - Uri verificationURI, ManifestDigest manifestDigest); + Uri verificationURI, ManifestDigest manifestDigest, + ContainerEncryptionParams encryptionParams); /** * Allows a package listening to the diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 727c094..c9f7a58 100755 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -18,6 +18,7 @@ package com.android.internal.app; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.content.pm.ContainerEncryptionParams; import android.content.pm.PackageInfoLite; import android.content.res.ObbInfo; @@ -25,9 +26,9 @@ interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, boolean isForwardLocked); - int copyResource(in Uri packageURI, - in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in Uri fileUri, in int flags, in long threshold); + int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, + in ParcelFileDescriptor outStream); + PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold); boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked); ObbInfo getObbInfo(in String filename); |