summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ApplicationPackageManager.java5
-rw-r--r--core/java/android/content/pm/ContainerEncryptionParams.aidl19
-rw-r--r--core/java/android/content/pm/ContainerEncryptionParams.java378
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/LimitedLengthInputStream.java82
-rw-r--r--core/java/android/content/pm/MacAuthenticatedInputStream.java78
-rw-r--r--core/java/android/content/pm/PackageManager.java12
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl7
-rw-r--r--core/tests/coretests/src/android/content/pm/ContainerEncryptionParamsTest.java370
-rw-r--r--core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java186
-rw-r--r--core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java120
11 files changed, 1251 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);
diff --git a/core/tests/coretests/src/android/content/pm/ContainerEncryptionParamsTest.java b/core/tests/coretests/src/android/content/pm/ContainerEncryptionParamsTest.java
new file mode 100644
index 0000000..7deaa9a
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/ContainerEncryptionParamsTest.java
@@ -0,0 +1,370 @@
+/*
+ * 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.test.AndroidTestCase;
+
+import java.util.Arrays;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+public class ContainerEncryptionParamsTest extends AndroidTestCase {
+ private static final String ENC_ALGORITHM = "AES/CBC/PKCS7Padding";
+
+ private static final byte[] IV_BYTES = "FOOBAR".getBytes();
+
+ private static final IvParameterSpec ENC_PARAMS = new IvParameterSpec(IV_BYTES);
+
+ private static final byte[] ENC_KEY_BYTES = "abcd1234wxyz7890".getBytes();
+
+ private static final SecretKey ENC_KEY = new SecretKeySpec(ENC_KEY_BYTES, "RAW");
+
+ private static final String MAC_ALGORITHM = "HMAC-SHA1";
+
+ private static final byte[] MAC_KEY_BYTES = "4wxyzabcd1237890".getBytes();
+
+ private static final SecretKey MAC_KEY = new SecretKeySpec(MAC_KEY_BYTES, "RAW");
+
+ private static final byte[] MAC_TAG = "faketag".getBytes();
+
+ private static final int AUTHENTICATED_START = 5;
+
+ private static final int ENCRYPTED_START = 11;
+
+ private static final int DATA_END = 19;
+
+ public void testParcel() throws Exception {
+ ContainerEncryptionParams expected = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ Parcel parcel = Parcel.obtain();
+ expected.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ ContainerEncryptionParams actual = ContainerEncryptionParams.CREATOR
+ .createFromParcel(parcel);
+
+ assertEquals(ENC_ALGORITHM, actual.getEncryptionAlgorithm());
+
+ if (!(actual.getEncryptionSpec() instanceof IvParameterSpec)) {
+ fail("encryption parameters should be IvParameterSpec");
+ } else {
+ IvParameterSpec actualParams = (IvParameterSpec) actual.getEncryptionSpec();
+ assertTrue(Arrays.equals(IV_BYTES, actualParams.getIV()));
+ }
+
+ assertEquals(ENC_KEY, actual.getEncryptionKey());
+
+ assertEquals(MAC_ALGORITHM, actual.getMacAlgorithm());
+
+ assertNull(actual.getMacSpec());
+
+ assertEquals(MAC_KEY, actual.getMacKey());
+
+ assertTrue(Arrays.equals(MAC_TAG, actual.getMacTag()));
+
+ assertEquals(AUTHENTICATED_START, actual.getAuthenticatedDataStart());
+
+ assertEquals(ENCRYPTED_START, actual.getEncryptedDataStart());
+
+ assertEquals(DATA_END, actual.getDataEnd());
+ }
+
+ public void testEquals_Success() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertEquals(params1, params2);
+ }
+
+ public void testEquals_EncAlgo_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(new String(
+ "AES-256/CBC/PKCS7Padding"), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_EncParams_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec("BLAHBLAH".getBytes()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_EncKey_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec("BLAHBLAH".getBytes(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_MacAlgo_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), "BLAHBLAH", null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_MacKey_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec("FAKE_MAC_KEY".getBytes(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_MacTag_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), "broken".getBytes(),
+ AUTHENTICATED_START, ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_AuthenticatedStart_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START - 1,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_EncryptedStart_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START - 1, DATA_END);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testEquals_DataEnd_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END + 1);
+
+ assertFalse(params1.equals(params2));
+ }
+
+ public void testHashCode_Success() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertEquals(params1.hashCode(), params2.hashCode());
+ }
+
+ public void testHashCode_EncAlgo_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(new String(
+ "AES-256/CBC/PKCS7Padding"), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_EncParams_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec("BLAHBLAH".getBytes()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_EncKey_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec("BLAHBLAH".getBytes(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_MacAlgo_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), "BLAHBLAH", null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_MacKey_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec("FAKE_MAC_KEY".getBytes(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_MacTag_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), "broken".getBytes(),
+ AUTHENTICATED_START, ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_AuthenticatedStart_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START - 1,
+ ENCRYPTED_START, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_EncryptedStart_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START - 1, DATA_END);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+
+ public void testHashCode_DataEnd_Failure() throws Exception {
+ ContainerEncryptionParams params1 = new ContainerEncryptionParams(ENC_ALGORITHM,
+ ENC_PARAMS, ENC_KEY, MAC_ALGORITHM, null, MAC_KEY, MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END);
+
+ ContainerEncryptionParams params2 = new ContainerEncryptionParams(
+ new String(ENC_ALGORITHM), new IvParameterSpec(IV_BYTES.clone()),
+ new SecretKeySpec(ENC_KEY_BYTES.clone(), "RAW"), new String(MAC_ALGORITHM), null,
+ new SecretKeySpec(MAC_KEY_BYTES.clone(), "RAW"), MAC_TAG, AUTHENTICATED_START,
+ ENCRYPTED_START, DATA_END + 1);
+
+ assertFalse(params1.hashCode() == params2.hashCode());
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
new file mode 100644
index 0000000..0a0152b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class LimitedLengthInputStreamTest extends AndroidTestCase {
+ private final byte[] TEST_STRING1 = "This is a test".getBytes();
+
+ private InputStream mTestStream1;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mTestStream1 = new ByteArrayInputStream(TEST_STRING1);
+ }
+
+ @MediumTest
+ public void testConstructor_NegativeOffset_Failure() throws Exception {
+ try {
+ InputStream is = new LimitedLengthInputStream(mTestStream1, -1, TEST_STRING1.length);
+ fail("Should throw IOException on negative index");
+ } catch (IOException e) {
+ // success
+ }
+ }
+
+ @MediumTest
+ public void testConstructor_NegativeLength_Failure() throws Exception {
+ try {
+ InputStream is = new LimitedLengthInputStream(mTestStream1, 0, -1);
+ fail("Should throw IOException on negative length");
+ } catch (IOException e) {
+ // success
+ }
+ }
+
+ @MediumTest
+ public void testConstructor_NullInputStream_Failure() throws Exception {
+ try {
+ InputStream is = new LimitedLengthInputStream(null, 0, 1);
+ fail("Should throw IOException on null input stream");
+ } catch (IOException e) {
+ // success
+ }
+ }
+
+ private void checkReadBytesWithOffsetAndLength_WithString1(int offset, int length)
+ throws Exception {
+ byte[] temp = new byte[TEST_STRING1.length];
+ byte[] expected = new byte[length];
+ byte[] actual = new byte[length];
+
+ System.arraycopy(TEST_STRING1, offset, expected, 0, length);
+
+ InputStream is = new LimitedLengthInputStream(mTestStream1, offset, length);
+ assertEquals(length, is.read(temp, 0, temp.length));
+
+ System.arraycopy(temp, 0, actual, 0, length);
+ assertTrue(Arrays.equals(expected, actual));
+
+ assertEquals(-1, is.read(temp, 0, temp.length));
+ }
+
+ @MediumTest
+ public void testReadBytesWithOffsetAndLength_ZeroOffset_PartialLength_Success()
+ throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(0, 2);
+ }
+
+ @MediumTest
+ public void testReadBytesWithOffsetAndLength_NonZeroOffset_PartialLength_Success()
+ throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(3, 2);
+ }
+
+ @MediumTest
+ public void testReadBytesWithOffsetAndLength_ZeroOffset_FullLength_Success() throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(0, TEST_STRING1.length);
+ }
+
+ @MediumTest
+ public void testReadBytesWithOffsetAndLength_NonZeroOffset_FullLength_Success()
+ throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(3, TEST_STRING1.length - 3);
+ }
+
+ @MediumTest
+ public void testReadBytesWithOffsetAndLength_ZeroOffset_PastEnd_Success() throws Exception {
+ byte[] temp = new byte[TEST_STRING1.length + 10];
+ InputStream is = new LimitedLengthInputStream(mTestStream1, 0, TEST_STRING1.length + 10);
+ assertEquals(TEST_STRING1.length, is.read(temp, 0, TEST_STRING1.length + 10));
+
+ byte[] actual = new byte[TEST_STRING1.length];
+ System.arraycopy(temp, 0, actual, 0, actual.length);
+ assertTrue(Arrays.equals(TEST_STRING1, actual));
+ }
+
+ private void checkReadBytes_WithString1(int offset, int length) throws Exception {
+ byte[] temp = new byte[TEST_STRING1.length];
+ byte[] expected = new byte[length];
+ byte[] actual = new byte[length];
+
+ System.arraycopy(TEST_STRING1, offset, expected, 0, length);
+
+ InputStream is = new LimitedLengthInputStream(mTestStream1, offset, length);
+ assertEquals(length, is.read(temp));
+
+ System.arraycopy(temp, 0, actual, 0, length);
+ assertTrue(Arrays.equals(expected, actual));
+
+ assertEquals(-1, is.read(temp));
+ }
+
+ @MediumTest
+ public void testReadBytes_ZeroOffset_PartialLength_Success() throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(0, 2);
+ }
+
+ @MediumTest
+ public void testReadBytes_NonZeroOffset_PartialLength_Success() throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(3, 2);
+ }
+
+ @MediumTest
+ public void testReadBytes_ZeroOffset_FullLength_Success() throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(0, TEST_STRING1.length);
+ }
+
+ @MediumTest
+ public void testReadBytes_NonZeroOffset_FullLength_Success() throws Exception {
+ checkReadBytesWithOffsetAndLength_WithString1(3, TEST_STRING1.length - 3);
+ }
+
+ private void checkSingleByteRead_WithString1(int offset, int length) throws Exception {
+ InputStream is = new LimitedLengthInputStream(mTestStream1, offset, length);
+
+ for (int i = 0; i < length; i++) {
+ assertEquals(TEST_STRING1[offset + i], is.read());
+ }
+
+ assertEquals(-1, is.read());
+ }
+
+ @MediumTest
+ public void testSingleByteRead_ZeroOffset_PartialLength_Success() throws Exception {
+ checkSingleByteRead_WithString1(0, 2);
+ }
+
+ @MediumTest
+ public void testSingleByteRead_NonZeroOffset_PartialLength_Success() throws Exception {
+ checkSingleByteRead_WithString1(3, 2);
+ }
+
+ @MediumTest
+ public void testSingleByteRead_ZeroOffset_FullLength_Success() throws Exception {
+ checkSingleByteRead_WithString1(0, TEST_STRING1.length);
+ }
+
+ @MediumTest
+ public void testSingleByteRead_NonZeroOffset_FullLength_Success() throws Exception {
+ checkSingleByteRead_WithString1(3, TEST_STRING1.length - 3);
+ }
+
+}
diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
new file mode 100644
index 0000000..948e722
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import libcore.io.Streams;
+
+public class MacAuthenticatedInputStreamTest extends AndroidTestCase {
+
+ private static final SecretKey HMAC_KEY_1 = new SecretKeySpec("test_key_1".getBytes(), "HMAC");
+
+ private static final byte[] TEST_STRING_1 = "Hello, World!".getBytes();
+
+ /**
+ * Generated with:
+ *
+ * echo -n 'Hello, World!' | openssl dgst -hmac 'test_key_1' -binary -sha1 | recode ..//x1 |
+ * sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] TEST_STRING_1_MAC = {
+ (byte) 0x29, (byte) 0xB1, (byte) 0x87, (byte) 0x6B, (byte) 0xFE, (byte) 0x83,
+ (byte) 0x96, (byte) 0x51, (byte) 0x61, (byte) 0x02, (byte) 0xAF, (byte) 0x7B,
+ (byte) 0xBA, (byte) 0x05, (byte) 0xE6, (byte) 0xA4, (byte) 0xAB, (byte) 0x36,
+ (byte) 0x18, (byte) 0x02
+ };
+
+ /**
+ * Same as TEST_STRING_1_MAC but with the first byte as 0x28 instead of
+ * 0x29.
+ */
+ private static final byte[] TEST_STRING_1_MAC_BROKEN = {
+ (byte) 0x28, (byte) 0xB1, (byte) 0x87, (byte) 0x6B, (byte) 0xFE, (byte) 0x83,
+ (byte) 0x96, (byte) 0x51, (byte) 0x61, (byte) 0x02, (byte) 0xAF, (byte) 0x7B,
+ (byte) 0xBA, (byte) 0x05, (byte) 0xE6, (byte) 0xA4, (byte) 0xAB, (byte) 0x36,
+ (byte) 0x18, (byte) 0x02
+ };
+
+ private ByteArrayInputStream mTestStream1;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mTestStream1 = new ByteArrayInputStream(TEST_STRING_1);
+ }
+
+ public void testString1Authenticate_Success() throws Exception {
+ Mac mac = Mac.getInstance("HMAC-SHA1");
+ mac.init(HMAC_KEY_1);
+
+ MacAuthenticatedInputStream is = new MacAuthenticatedInputStream(mTestStream1, mac);
+
+ assertTrue(Arrays.equals(TEST_STRING_1, Streams.readFully(is)));
+
+ assertTrue(is.isTagEqual(TEST_STRING_1_MAC));
+ }
+
+ public void testString1Authenticate_WrongTag_Failure() throws Exception {
+ Mac mac = Mac.getInstance("HMAC-SHA1");
+ mac.init(HMAC_KEY_1);
+
+ MacAuthenticatedInputStream is = new MacAuthenticatedInputStream(mTestStream1, mac);
+
+ assertTrue(Arrays.equals(TEST_STRING_1, Streams.readFully(is)));
+
+ assertFalse(is.isTagEqual(TEST_STRING_1_MAC_BROKEN));
+ }
+
+ public void testString1Authenticate_NullTag_Failure() throws Exception {
+ Mac mac = Mac.getInstance("HMAC-SHA1");
+ mac.init(HMAC_KEY_1);
+
+ MacAuthenticatedInputStream is = new MacAuthenticatedInputStream(mTestStream1, mac);
+
+ assertTrue(Arrays.equals(TEST_STRING_1, Streams.readFully(is)));
+
+ assertFalse(is.isTagEqual(null));
+ }
+
+ public void testString1Authenticate_ReadSingleByte_Success() throws Exception {
+ Mac mac = Mac.getInstance("HMAC-SHA1");
+ mac.init(HMAC_KEY_1);
+
+ MacAuthenticatedInputStream is = new MacAuthenticatedInputStream(mTestStream1, mac);
+
+ int numRead = 0;
+ while (is.read() != -1) {
+ numRead++;
+
+ if (numRead > TEST_STRING_1.length) {
+ fail("read too many bytes");
+ }
+ }
+ assertEquals(TEST_STRING_1.length, numRead);
+
+ assertTrue(is.isTagEqual(TEST_STRING_1_MAC));
+ }
+}