Merge "Add encryption parameters to package installation" into jb-dev
This commit is contained in:
@ -16,9 +16,12 @@
|
||||
|
||||
package com.android.commands.pm;
|
||||
|
||||
import com.android.internal.content.PackageHelper;
|
||||
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ContainerEncryptionParams;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
@ -40,17 +43,20 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.android.internal.content.PackageHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public final class Pm {
|
||||
IPackageManager mPm;
|
||||
|
||||
@ -763,6 +769,15 @@ public final class Pm {
|
||||
String installerPackageName = null;
|
||||
|
||||
String opt;
|
||||
|
||||
String algo = null;
|
||||
byte[] iv = null;
|
||||
byte[] key = null;
|
||||
|
||||
String macAlgo = null;
|
||||
byte[] macKey = null;
|
||||
byte[] tag = null;
|
||||
|
||||
while ((opt=nextOption()) != null) {
|
||||
if (opt.equals("-l")) {
|
||||
installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
|
||||
@ -783,6 +798,48 @@ public final class Pm {
|
||||
} else if (opt.equals("-f")) {
|
||||
// Override if -s option is specified.
|
||||
installFlags |= PackageManager.INSTALL_INTERNAL;
|
||||
} else if (opt.equals("--algo")) {
|
||||
algo = nextOptionData();
|
||||
if (algo == null) {
|
||||
System.err.println("Error: must supply argument for --algo");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else if (opt.equals("--iv")) {
|
||||
iv = hexToBytes(nextOptionData());
|
||||
if (iv == null) {
|
||||
System.err.println("Error: must supply argument for --iv");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else if (opt.equals("--key")) {
|
||||
key = hexToBytes(nextOptionData());
|
||||
if (key == null) {
|
||||
System.err.println("Error: must supply argument for --key");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else if (opt.equals("--macalgo")) {
|
||||
macAlgo = nextOptionData();
|
||||
if (macAlgo == null) {
|
||||
System.err.println("Error: must supply argument for --macalgo");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else if (opt.equals("--mackey")) {
|
||||
macKey = hexToBytes(nextOptionData());
|
||||
if (macKey == null) {
|
||||
System.err.println("Error: must supply argument for --mackey");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else if (opt.equals("--tag")) {
|
||||
tag = hexToBytes(nextOptionData());
|
||||
if (tag == null) {
|
||||
System.err.println("Error: must supply argument for --tag");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
System.err.println("Error: Unknown option: " + opt);
|
||||
showUsage();
|
||||
@ -790,6 +847,44 @@ public final class Pm {
|
||||
}
|
||||
}
|
||||
|
||||
final ContainerEncryptionParams encryptionParams;
|
||||
if (algo != null || iv != null || key != null || macAlgo != null || macKey != null
|
||||
|| tag != null) {
|
||||
if (algo == null || iv == null || key == null) {
|
||||
System.err.println("Error: all of --algo, --iv, and --key must be specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (macAlgo != null || macKey != null || tag != null) {
|
||||
if (macAlgo == null || macKey == null || tag == null) {
|
||||
System.err.println("Error: all of --macalgo, --mackey, and --tag must "
|
||||
+ "be specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final SecretKey encKey = new SecretKeySpec(key, "RAW");
|
||||
|
||||
final SecretKey macSecretKey;
|
||||
if (macKey == null || macKey.length == 0) {
|
||||
macSecretKey = null;
|
||||
} else {
|
||||
macSecretKey = new SecretKeySpec(macKey, "RAW");
|
||||
}
|
||||
|
||||
encryptionParams = new ContainerEncryptionParams(algo, new IvParameterSpec(iv),
|
||||
encKey, macAlgo, null, macSecretKey, tag, -1, -1, -1);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
encryptionParams = null;
|
||||
}
|
||||
|
||||
final Uri apkURI;
|
||||
final Uri verificationURI;
|
||||
|
||||
@ -816,7 +911,7 @@ public final class Pm {
|
||||
PackageInstallObserver obs = new PackageInstallObserver();
|
||||
try {
|
||||
mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName,
|
||||
verificationURI, null);
|
||||
verificationURI, null, encryptionParams);
|
||||
|
||||
synchronized (obs) {
|
||||
while (!obs.finished) {
|
||||
@ -839,6 +934,37 @@ public final class Pm {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing hex-encoded bytes to a byte array.
|
||||
*
|
||||
* @param input String containing hex-encoded bytes
|
||||
* @return input as an array of bytes
|
||||
*/
|
||||
private byte[] hexToBytes(String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int inputLength = input.length();
|
||||
if ((inputLength % 2) != 0) {
|
||||
System.err.print("Invalid length; must be multiple of 2");
|
||||
return null;
|
||||
}
|
||||
|
||||
final int byteLength = inputLength / 2;
|
||||
final byte[] output = new byte[byteLength];
|
||||
|
||||
int inputIndex = 0;
|
||||
int byteIndex = 0;
|
||||
while (inputIndex < inputLength) {
|
||||
output[byteIndex++] = (byte) Integer.parseInt(
|
||||
input.substring(inputIndex, inputIndex + 2), 16);
|
||||
inputIndex += 2;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public void runCreateUser() {
|
||||
// Need to be run as root
|
||||
if (Process.myUid() != ROOT_UID) {
|
||||
@ -1236,7 +1362,8 @@ public final class Pm {
|
||||
System.err.println(" pm list libraries");
|
||||
System.err.println(" pm list users");
|
||||
System.err.println(" pm path PACKAGE");
|
||||
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
|
||||
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
|
||||
System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>] PATH");
|
||||
System.err.println(" pm uninstall [-k] PACKAGE");
|
||||
System.err.println(" pm clear PACKAGE");
|
||||
System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
|
||||
|
@ -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!
|
||||
}
|
||||
|
19
core/java/android/content/pm/ContainerEncryptionParams.aidl
Normal file
19
core/java/android/content/pm/ContainerEncryptionParams.aidl
Normal file
@ -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;
|
378
core/java/android/content/pm/ContainerEncryptionParams.java
Normal file
378
core/java/android/content/pm/ContainerEncryptionParams.java
Normal file
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
@ -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);
|
||||
|
||||
|
82
core/java/android/content/pm/LimitedLengthInputStream.java
Normal file
82
core/java/android/content/pm/LimitedLengthInputStream.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -22,7 +22,10 @@ import com.android.internal.content.PackageHelper;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.MacAuthenticatedInputStream;
|
||||
import android.content.pm.ContainerEncryptionParams;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.LimitedLengthInputStream;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInfoLite;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -49,9 +52,21 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.DigestException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import libcore.io.ErrnoException;
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.io.Libcore;
|
||||
import libcore.io.Streams;
|
||||
import libcore.io.StructStatFs;
|
||||
|
||||
/*
|
||||
@ -68,7 +83,7 @@ public class DefaultContainerService extends IntentService {
|
||||
private static final String LIB_DIR_NAME = "lib";
|
||||
|
||||
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
|
||||
/*
|
||||
/**
|
||||
* Creates a new container and copies resource there.
|
||||
* @param paackageURI the uri of resource to be copied. Can be either
|
||||
* a content uri or a file uri
|
||||
@ -92,15 +107,19 @@ public class DefaultContainerService extends IntentService {
|
||||
isExternal, isForwardLocked);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Copy specified resource to output stream
|
||||
*
|
||||
* @param packageURI the uri of resource to be copied. Should be a file
|
||||
* uri
|
||||
* uri
|
||||
* @param encryptionParams parameters describing the encryption used for
|
||||
* this file
|
||||
* @param outStream Remote file descriptor to be used for copying
|
||||
* @return returns status code according to those in {@link
|
||||
* PackageManager}
|
||||
* @return returns status code according to those in
|
||||
* {@link PackageManager}
|
||||
*/
|
||||
public int copyResource(final Uri packageURI, ParcelFileDescriptor outStream) {
|
||||
public int copyResource(final Uri packageURI, ContainerEncryptionParams encryptionParams,
|
||||
ParcelFileDescriptor outStream) {
|
||||
if (packageURI == null || outStream == null) {
|
||||
return PackageManager.INSTALL_FAILED_INVALID_URI;
|
||||
}
|
||||
@ -109,7 +128,7 @@ public class DefaultContainerService extends IntentService {
|
||||
= new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
|
||||
|
||||
try {
|
||||
copyFile(packageURI, autoOut);
|
||||
copyFile(packageURI, autoOut, encryptionParams);
|
||||
return PackageManager.INSTALL_SUCCEEDED;
|
||||
} catch (FileNotFoundException e) {
|
||||
Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " FNF: "
|
||||
@ -119,10 +138,14 @@ public class DefaultContainerService extends IntentService {
|
||||
Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " IO: "
|
||||
+ e.getMessage());
|
||||
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
|
||||
} catch (DigestException e) {
|
||||
Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " Security: "
|
||||
+ e.getMessage());
|
||||
return PackageManager.INSTALL_FAILED_INVALID_APK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Determine the recommended install location for package
|
||||
* specified by file uri location.
|
||||
* @param fileUri the uri of resource to be copied. Should be a
|
||||
@ -130,28 +153,24 @@ public class DefaultContainerService extends IntentService {
|
||||
* @return Returns PackageInfoLite object containing
|
||||
* the package info and recommended app location.
|
||||
*/
|
||||
public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) {
|
||||
public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
|
||||
long threshold) {
|
||||
PackageInfoLite ret = new PackageInfoLite();
|
||||
if (fileUri == null) {
|
||||
Slog.i(TAG, "Invalid package uri " + fileUri);
|
||||
|
||||
if (packagePath == null) {
|
||||
Slog.i(TAG, "Invalid package file " + packagePath);
|
||||
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
|
||||
return ret;
|
||||
}
|
||||
String scheme = fileUri.getScheme();
|
||||
if (scheme != null && !scheme.equals("file")) {
|
||||
Slog.w(TAG, "Falling back to installing on internal storage only");
|
||||
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
|
||||
return ret;
|
||||
}
|
||||
String archiveFilePath = fileUri.getPath();
|
||||
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
metrics.setToDefaults();
|
||||
|
||||
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0);
|
||||
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packagePath, 0);
|
||||
if (pkg == null) {
|
||||
Slog.w(TAG, "Failed to parse package");
|
||||
|
||||
final File apkFile = new File(archiveFilePath);
|
||||
final File apkFile = new File(packagePath);
|
||||
if (!apkFile.exists()) {
|
||||
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
|
||||
} else {
|
||||
@ -160,12 +179,13 @@ public class DefaultContainerService extends IntentService {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.packageName = pkg.packageName;
|
||||
ret.installLocation = pkg.installLocation;
|
||||
ret.verifiers = pkg.verifiers;
|
||||
|
||||
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
|
||||
archiveFilePath, flags, threshold);
|
||||
packagePath, flags, threshold);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -392,55 +412,195 @@ public class DefaultContainerService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyToFile(File srcFile, OutputStream out)
|
||||
throws FileNotFoundException, IOException {
|
||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile));
|
||||
private void copyFile(Uri pPackageURI, OutputStream outStream,
|
||||
ContainerEncryptionParams encryptionParams) throws FileNotFoundException, IOException,
|
||||
DigestException {
|
||||
String scheme = pPackageURI.getScheme();
|
||||
InputStream inStream = null;
|
||||
try {
|
||||
copyToFile(inputStream, out);
|
||||
if (scheme == null || scheme.equals("file")) {
|
||||
final InputStream is = new FileInputStream(new File(pPackageURI.getPath()));
|
||||
inStream = new BufferedInputStream(is);
|
||||
} else if (scheme.equals("content")) {
|
||||
final ParcelFileDescriptor fd;
|
||||
try {
|
||||
fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
Slog.e(TAG, "Couldn't open file descriptor from download service. "
|
||||
+ "Failed with exception " + e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (fd == null) {
|
||||
Slog.e(TAG, "Provider returned no file descriptor for " +
|
||||
pPackageURI.toString());
|
||||
throw new FileNotFoundException("provider returned no file descriptor");
|
||||
} else {
|
||||
if (localLOGV) {
|
||||
Slog.i(TAG, "Opened file descriptor from download service.");
|
||||
}
|
||||
inStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
|
||||
throw new FileNotFoundException("Package URI is not 'file:' or 'content:'");
|
||||
}
|
||||
|
||||
/*
|
||||
* If this resource is encrypted, get the decrypted stream version
|
||||
* of it.
|
||||
*/
|
||||
ApkContainer container = new ApkContainer(inStream, encryptionParams);
|
||||
|
||||
try {
|
||||
/*
|
||||
* We copy the source package file to a temp file and then
|
||||
* rename it to the destination file in order to eliminate a
|
||||
* window where the package directory scanner notices the new
|
||||
* package file but it's not completely copied yet.
|
||||
*/
|
||||
copyToFile(container.getInputStream(), outStream);
|
||||
|
||||
if (!container.isAuthenticated()) {
|
||||
throw new DigestException();
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new DigestException("A problem occured copying the file.");
|
||||
}
|
||||
} finally {
|
||||
try { inputStream.close(); } catch (IOException e) {}
|
||||
IoUtils.closeQuietly(inStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(Uri pPackageURI, OutputStream outStream) throws FileNotFoundException,
|
||||
IOException {
|
||||
String scheme = pPackageURI.getScheme();
|
||||
if (scheme == null || scheme.equals("file")) {
|
||||
final File srcPackageFile = new File(pPackageURI.getPath());
|
||||
// We copy the source package file to a temp file and then rename it to the
|
||||
// destination file in order to eliminate a window where the package directory
|
||||
// scanner notices the new package file but it's not completely copied yet.
|
||||
copyToFile(srcPackageFile, outStream);
|
||||
} else if (scheme.equals("content")) {
|
||||
ParcelFileDescriptor fd = null;
|
||||
try {
|
||||
fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
Slog.e(TAG, "Couldn't open file descriptor from download service. "
|
||||
+ "Failed with exception " + e);
|
||||
throw e;
|
||||
}
|
||||
private static class ApkContainer {
|
||||
private final InputStream mInStream;
|
||||
|
||||
if (fd == null) {
|
||||
Slog.e(TAG, "Provider returned no file descriptor for " + pPackageURI.toString());
|
||||
throw new FileNotFoundException("provider returned no file descriptor");
|
||||
private MacAuthenticatedInputStream mAuthenticatedStream;
|
||||
|
||||
private byte[] mTag;
|
||||
|
||||
public ApkContainer(InputStream inStream, ContainerEncryptionParams encryptionParams)
|
||||
throws IOException {
|
||||
if (encryptionParams == null) {
|
||||
mInStream = inStream;
|
||||
} else {
|
||||
if (localLOGV) {
|
||||
Slog.i(TAG, "Opened file descriptor from download service.");
|
||||
}
|
||||
ParcelFileDescriptor.AutoCloseInputStream dlStream
|
||||
= new ParcelFileDescriptor.AutoCloseInputStream(fd);
|
||||
|
||||
// We copy the source package file to a temp file and then rename it to the
|
||||
// destination file in order to eliminate a window where the package directory
|
||||
// scanner notices the new package file but it's not completely
|
||||
// copied
|
||||
copyToFile(dlStream, outStream);
|
||||
mInStream = getDecryptedStream(inStream, encryptionParams);
|
||||
mTag = encryptionParams.getMacTag();
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
|
||||
throw new FileNotFoundException("Package URI is not 'file:' or 'content:'");
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
if (mAuthenticatedStream == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mAuthenticatedStream.isTagEqual(mTag);
|
||||
}
|
||||
|
||||
private Mac getMacInstance(ContainerEncryptionParams encryptionParams) throws IOException {
|
||||
final Mac m;
|
||||
try {
|
||||
final String macAlgo = encryptionParams.getMacAlgorithm();
|
||||
|
||||
if (macAlgo != null) {
|
||||
m = Mac.getInstance(macAlgo);
|
||||
m.init(encryptionParams.getMacKey(), encryptionParams.getMacSpec());
|
||||
} else {
|
||||
m = null;
|
||||
}
|
||||
|
||||
return m;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return mInStream;
|
||||
}
|
||||
|
||||
private InputStream getDecryptedStream(InputStream inStream,
|
||||
ContainerEncryptionParams encryptionParams) throws IOException {
|
||||
final Cipher c;
|
||||
try {
|
||||
c = Cipher.getInstance(encryptionParams.getEncryptionAlgorithm());
|
||||
c.init(Cipher.DECRYPT_MODE, encryptionParams.getEncryptionKey(),
|
||||
encryptionParams.getEncryptionSpec());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException(e);
|
||||
} catch (NoSuchPaddingException e) {
|
||||
throw new IOException(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
final int encStart = encryptionParams.getEncryptedDataStart();
|
||||
final int end = encryptionParams.getDataEnd();
|
||||
if (end < encStart) {
|
||||
throw new IOException("end <= encStart");
|
||||
}
|
||||
|
||||
final Mac mac = getMacInstance(encryptionParams);
|
||||
if (mac != null) {
|
||||
final int macStart = encryptionParams.getAuthenticatedDataStart();
|
||||
|
||||
final int furtherOffset;
|
||||
if (macStart >= 0 && encStart >= 0 && macStart < encStart) {
|
||||
/*
|
||||
* If there is authenticated data at the beginning, read
|
||||
* that into our MAC first.
|
||||
*/
|
||||
final int authenticatedLength = encStart - macStart;
|
||||
final byte[] authenticatedData = new byte[authenticatedLength];
|
||||
|
||||
Streams.readFully(inStream, authenticatedData, macStart, authenticatedLength);
|
||||
mac.update(authenticatedData, 0, authenticatedLength);
|
||||
|
||||
furtherOffset = 0;
|
||||
} else {
|
||||
/*
|
||||
* No authenticated data at the beginning. Just skip the
|
||||
* required number of bytes to the beginning of the stream.
|
||||
*/
|
||||
if (encStart > 0) {
|
||||
furtherOffset = encStart;
|
||||
} else {
|
||||
furtherOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is data at the end of the stream we want to ignore,
|
||||
* wrap this in a LimitedLengthInputStream.
|
||||
*/
|
||||
if (furtherOffset >= 0 && end > furtherOffset) {
|
||||
inStream = new LimitedLengthInputStream(inStream, furtherOffset, end - encStart);
|
||||
} else if (furtherOffset > 0) {
|
||||
inStream.skip(furtherOffset);
|
||||
}
|
||||
|
||||
mAuthenticatedStream = new MacAuthenticatedInputStream(inStream, mac);
|
||||
|
||||
inStream = mAuthenticatedStream;
|
||||
} else {
|
||||
if (encStart >= 0) {
|
||||
if (end > encStart) {
|
||||
inStream = new LimitedLengthInputStream(inStream, encStart, end - encStart);
|
||||
} else {
|
||||
inStream.skip(encStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CipherInputStream(inStream, c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final int PREFER_INTERNAL = 1;
|
||||
|
@ -53,6 +53,7 @@ import android.content.ServiceConnection;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ContainerEncryptionParams;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
@ -116,7 +117,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
@ -128,7 +128,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -136,9 +135,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import libcore.io.ErrnoException;
|
||||
import libcore.io.IoUtils;
|
||||
@ -5133,13 +5129,13 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
final Uri packageURI, final IPackageInstallObserver observer, final int flags,
|
||||
final String installerPackageName) {
|
||||
installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
|
||||
null);
|
||||
null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
|
||||
int flags, String installerPackageName, Uri verificationURI,
|
||||
ManifestDigest manifestDigest) {
|
||||
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
|
||||
|
||||
final int uid = Binder.getCallingUid();
|
||||
@ -5157,7 +5153,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
|
||||
final Message msg = mHandler.obtainMessage(INIT_COPY);
|
||||
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
|
||||
verificationURI, manifestDigest);
|
||||
verificationURI, manifestDigest, encryptionParams);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
@ -5560,22 +5556,27 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
class InstallParams extends HandlerParams {
|
||||
final IPackageInstallObserver observer;
|
||||
int flags;
|
||||
final Uri packageURI;
|
||||
|
||||
private final Uri mPackageURI;
|
||||
final String installerPackageName;
|
||||
final Uri verificationURI;
|
||||
final ManifestDigest manifestDigest;
|
||||
private InstallArgs mArgs;
|
||||
private int mRet;
|
||||
private File mTempPackage;
|
||||
final ContainerEncryptionParams encryptionParams;
|
||||
|
||||
InstallParams(Uri packageURI,
|
||||
IPackageInstallObserver observer, int flags,
|
||||
String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest) {
|
||||
this.packageURI = packageURI;
|
||||
String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest,
|
||||
ContainerEncryptionParams encryptionParams) {
|
||||
this.mPackageURI = packageURI;
|
||||
this.flags = flags;
|
||||
this.observer = observer;
|
||||
this.installerPackageName = installerPackageName;
|
||||
this.verificationURI = verificationURI;
|
||||
this.manifestDigest = manifestDigest;
|
||||
this.encryptionParams = encryptionParams;
|
||||
}
|
||||
|
||||
private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
|
||||
@ -5655,16 +5656,51 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
lowThreshold = dsm.getMemoryLowThreshold();
|
||||
}
|
||||
|
||||
// Remote call to find out default install location
|
||||
try {
|
||||
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
|
||||
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags,
|
||||
lowThreshold);
|
||||
} finally {
|
||||
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
final File packageFile;
|
||||
if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) {
|
||||
ParcelFileDescriptor out = null;
|
||||
|
||||
mTempPackage = createTempPackageFile(mDrmAppPrivateInstallDir);
|
||||
if (mTempPackage != null) {
|
||||
try {
|
||||
out = ParcelFileDescriptor.open(mTempPackage,
|
||||
ParcelFileDescriptor.MODE_READ_WRITE);
|
||||
} catch (FileNotFoundException e) {
|
||||
Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI);
|
||||
}
|
||||
|
||||
// Make a temporary file for decryption.
|
||||
ret = mContainerService
|
||||
.copyResource(mPackageURI, encryptionParams, out);
|
||||
|
||||
packageFile = mTempPackage;
|
||||
|
||||
FileUtils.setPermissions(packageFile.getAbsolutePath(),
|
||||
FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IROTH,
|
||||
-1, -1);
|
||||
} else {
|
||||
packageFile = null;
|
||||
}
|
||||
} else {
|
||||
packageFile = new File(mPackageURI.getPath());
|
||||
}
|
||||
|
||||
if (packageFile != null) {
|
||||
// Remote call to find out default install location
|
||||
pkgLite = mContainerService.getMinimalPackageInfo(
|
||||
packageFile.getAbsolutePath(), flags, lowThreshold);
|
||||
}
|
||||
} finally {
|
||||
mContext.revokeUriPermission(mPackageURI,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == PackageManager.INSTALL_SUCCEEDED) {
|
||||
int loc = pkgLite.recommendedInstallLocation;
|
||||
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
|
||||
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
|
||||
@ -5708,8 +5744,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
final int requiredUid = mRequiredVerifierPackage == null ? -1
|
||||
: getPackageUid(mRequiredVerifierPackage, 0);
|
||||
if (requiredUid != -1 && isVerificationEnabled()) {
|
||||
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
|
||||
verification.setDataAndType(packageURI, PACKAGE_MIME_TYPE);
|
||||
final Intent verification = new Intent(
|
||||
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
|
||||
verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
|
||||
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
|
||||
@ -5812,6 +5849,13 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
if (mArgs != null) {
|
||||
processPendingInstall(mArgs, mRet);
|
||||
}
|
||||
|
||||
if (mTempPackage != null) {
|
||||
if (!mTempPackage.delete()) {
|
||||
Slog.w(TAG, "Couldn't delete temporary file: "
|
||||
+ mTempPackage.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -5823,6 +5867,14 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
public boolean isForwardLocked() {
|
||||
return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
|
||||
}
|
||||
|
||||
public Uri getPackageUri() {
|
||||
if (mTempPackage != null) {
|
||||
return Uri.fromFile(mTempPackage);
|
||||
} else {
|
||||
return mPackageURI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6037,8 +6089,8 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
boolean created = false;
|
||||
|
||||
FileInstallArgs(InstallParams params) {
|
||||
super(params.packageURI, params.observer, params.flags, params.installerPackageName,
|
||||
params.manifestDigest);
|
||||
super(params.getPackageUri(), params.observer, params.flags,
|
||||
params.installerPackageName, params.manifestDigest);
|
||||
}
|
||||
|
||||
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
|
||||
@ -6128,7 +6180,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
try {
|
||||
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
ret = imcs.copyResource(packageURI, out);
|
||||
ret = imcs.copyResource(packageURI, null, out);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(out);
|
||||
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
@ -6315,8 +6367,8 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
String libraryPath;
|
||||
|
||||
AsecInstallArgs(InstallParams params) {
|
||||
super(params.packageURI, params.observer, params.flags, params.installerPackageName,
|
||||
params.manifestDigest);
|
||||
super(params.getPackageUri(), params.observer, params.flags,
|
||||
params.installerPackageName, params.manifestDigest);
|
||||
}
|
||||
|
||||
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
|
||||
|
@ -22,6 +22,7 @@ import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ContainerEncryptionParams;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
@ -29,6 +30,7 @@ import android.content.pm.IPackageInstallObserver;
|
||||
import android.content.pm.IPackageMoveObserver;
|
||||
import android.content.pm.IPackageStatsObserver;
|
||||
import android.content.pm.InstrumentationInfo;
|
||||
import android.content.pm.ManifestDigest;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionGroupInfo;
|
||||
@ -36,16 +38,12 @@ import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.ManifestDigest;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VerifierInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -565,7 +563,7 @@ public class MockPackageManager extends PackageManager {
|
||||
@Override
|
||||
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
|
||||
int flags, String installerPackageName, Uri verificationURI,
|
||||
ManifestDigest manifestDigest) {
|
||||
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user