Merge "OBB: use PBKDF2 for key generation." into gingerbread
This commit is contained in:
@@ -48,6 +48,13 @@ public class ObbInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public int flags;
|
public int flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The salt for the encryption algorithm.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public byte[] salt;
|
||||||
|
|
||||||
// Only allow things in this package to instantiate.
|
// Only allow things in this package to instantiate.
|
||||||
/* package */ ObbInfo() {
|
/* package */ ObbInfo() {
|
||||||
}
|
}
|
||||||
@@ -75,6 +82,7 @@ public class ObbInfo implements Parcelable {
|
|||||||
dest.writeString(packageName);
|
dest.writeString(packageName);
|
||||||
dest.writeInt(version);
|
dest.writeInt(version);
|
||||||
dest.writeInt(flags);
|
dest.writeInt(flags);
|
||||||
|
dest.writeByteArray(salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<ObbInfo> CREATOR
|
public static final Parcelable.Creator<ObbInfo> CREATOR
|
||||||
@@ -93,5 +101,6 @@ public class ObbInfo implements Parcelable {
|
|||||||
packageName = source.readString();
|
packageName = source.readString();
|
||||||
version = source.readInt();
|
version = source.readInt();
|
||||||
flags = source.readInt();
|
flags = source.readInt();
|
||||||
|
salt = source.createByteArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ static struct {
|
|||||||
jfieldID packageName;
|
jfieldID packageName;
|
||||||
jfieldID version;
|
jfieldID version;
|
||||||
jfieldID flags;
|
jfieldID flags;
|
||||||
|
jfieldID salt;
|
||||||
} gObbInfoClassInfo;
|
} gObbInfoClassInfo;
|
||||||
|
|
||||||
static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
|
static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
|
||||||
@@ -69,6 +70,14 @@ static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz
|
|||||||
env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
|
env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
|
||||||
env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
|
env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
|
||||||
env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
|
env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
|
||||||
|
|
||||||
|
size_t saltLen;
|
||||||
|
const unsigned char* salt = obb->getSalt(&saltLen);
|
||||||
|
if (saltLen > 0) {
|
||||||
|
jbyteArray saltArray = env->NewByteArray(saltLen);
|
||||||
|
env->SetByteArrayRegion(saltArray, 0, saltLen, (jbyte*)salt);
|
||||||
|
env->SetObjectField(obbInfo, gObbInfoClassInfo.salt, saltArray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -99,6 +108,8 @@ int register_android_content_res_ObbScanner(JNIEnv* env)
|
|||||||
"version", "I");
|
"version", "I");
|
||||||
GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
|
GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
|
||||||
"flags", "I");
|
"flags", "I");
|
||||||
|
GET_FIELD_ID(gObbInfoClassInfo.salt, gObbInfoClassInfo.clazz,
|
||||||
|
"salt", "[B");
|
||||||
|
|
||||||
return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
|
return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
|
||||||
NELEM(gMethods));
|
NELEM(gMethods));
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -27,6 +27,7 @@ namespace android {
|
|||||||
|
|
||||||
// OBB flags (bit 0)
|
// OBB flags (bit 0)
|
||||||
#define OBB_OVERLAY (1 << 0)
|
#define OBB_OVERLAY (1 << 0)
|
||||||
|
#define OBB_SALTED (1 << 1)
|
||||||
|
|
||||||
class ObbFile : public RefBase {
|
class ObbFile : public RefBase {
|
||||||
protected:
|
protected:
|
||||||
@@ -70,6 +71,26 @@ public:
|
|||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unsigned char* getSalt(size_t* length) const {
|
||||||
|
if ((mFlags & OBB_SALTED) == 0) {
|
||||||
|
*length = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*length = sizeof(mSalt);
|
||||||
|
return mSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setSalt(const unsigned char* salt, size_t length) {
|
||||||
|
if (length != sizeof(mSalt)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mSalt, salt, sizeof(mSalt));
|
||||||
|
mFlags |= OBB_SALTED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool isOverlay() {
|
bool isOverlay() {
|
||||||
return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
|
return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
|
||||||
}
|
}
|
||||||
@@ -103,6 +124,12 @@ private:
|
|||||||
/* Flags for this OBB type. */
|
/* Flags for this OBB type. */
|
||||||
int32_t mFlags;
|
int32_t mFlags;
|
||||||
|
|
||||||
|
/* Whether the file is salted. */
|
||||||
|
bool mSalted;
|
||||||
|
|
||||||
|
/* The encryption salt. */
|
||||||
|
unsigned char mSalt[8];
|
||||||
|
|
||||||
const char* mFileName;
|
const char* mFileName;
|
||||||
|
|
||||||
size_t mFileSize;
|
size_t mFileSize;
|
||||||
|
|||||||
@@ -29,10 +29,11 @@
|
|||||||
|
|
||||||
#define kFooterTagSize 8 /* last two 32-bit integers */
|
#define kFooterTagSize 8 /* last two 32-bit integers */
|
||||||
|
|
||||||
#define kFooterMinSize 25 /* 32-bit signature version (4 bytes)
|
#define kFooterMinSize 33 /* 32-bit signature version (4 bytes)
|
||||||
* 32-bit package version (4 bytes)
|
* 32-bit package version (4 bytes)
|
||||||
* 32-bit flags (4 bytes)
|
* 32-bit flags (4 bytes)
|
||||||
* 32-bit package name size (4-bytes)
|
* 64-bit salt (8 bytes)
|
||||||
|
* 32-bit package name size (4 bytes)
|
||||||
* >=1-character package name (1 byte)
|
* >=1-character package name (1 byte)
|
||||||
* 32-bit footer size (4 bytes)
|
* 32-bit footer size (4 bytes)
|
||||||
* 32-bit footer marker (4 bytes)
|
* 32-bit footer marker (4 bytes)
|
||||||
@@ -47,8 +48,9 @@
|
|||||||
/* offsets in version 1 of the header */
|
/* offsets in version 1 of the header */
|
||||||
#define kPackageVersionOffset 4
|
#define kPackageVersionOffset 4
|
||||||
#define kFlagsOffset 8
|
#define kFlagsOffset 8
|
||||||
#define kPackageNameLenOffset 12
|
#define kSaltOffset 12
|
||||||
#define kPackageNameOffset 16
|
#define kPackageNameLenOffset 20
|
||||||
|
#define kPackageNameOffset 24
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
|
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
|
||||||
@@ -79,11 +81,12 @@ typedef off64_t my_off64_t;
|
|||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
ObbFile::ObbFile() :
|
ObbFile::ObbFile()
|
||||||
mPackageName(""),
|
: mPackageName("")
|
||||||
mVersion(-1),
|
, mVersion(-1)
|
||||||
mFlags(0)
|
, mFlags(0)
|
||||||
{
|
{
|
||||||
|
memset(mSalt, 0, sizeof(mSalt));
|
||||||
}
|
}
|
||||||
|
|
||||||
ObbFile::~ObbFile() {
|
ObbFile::~ObbFile() {
|
||||||
@@ -192,7 +195,7 @@ bool ObbFile::parseObbFile(int fd)
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
for (int i = 0; i < footerSize; ++i) {
|
for (int i = 0; i < footerSize; ++i) {
|
||||||
LOGI("char: 0x%02x", scanBuf[i]);
|
LOGI("char: 0x%02x\n", scanBuf[i]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -206,6 +209,8 @@ bool ObbFile::parseObbFile(int fd)
|
|||||||
mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
|
mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
|
||||||
mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
|
mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
|
||||||
|
|
||||||
|
memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt));
|
||||||
|
|
||||||
uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
|
uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
|
||||||
if (packageNameLen <= 0
|
if (packageNameLen <= 0
|
||||||
|| packageNameLen > (footerSize - kPackageNameOffset)) {
|
|| packageNameLen > (footerSize - kPackageNameOffset)) {
|
||||||
@@ -255,7 +260,7 @@ bool ObbFile::writeTo(int fd)
|
|||||||
my_lseek64(fd, 0, SEEK_END);
|
my_lseek64(fd, 0, SEEK_END);
|
||||||
|
|
||||||
if (mPackageName.size() == 0 || mVersion == -1) {
|
if (mPackageName.size() == 0 || mVersion == -1) {
|
||||||
LOGW("tried to write uninitialized ObbFile data");
|
LOGW("tried to write uninitialized ObbFile data\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,43 +269,48 @@ bool ObbFile::writeTo(int fd)
|
|||||||
|
|
||||||
put4LE(intBuf, kSigVersion);
|
put4LE(intBuf, kSigVersion);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write signature version: %s", strerror(errno));
|
LOGW("couldn't write signature version: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
put4LE(intBuf, mVersion);
|
put4LE(intBuf, mVersion);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write package version");
|
LOGW("couldn't write package version\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
put4LE(intBuf, mFlags);
|
put4LE(intBuf, mFlags);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write package version");
|
LOGW("couldn't write package version\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) {
|
||||||
|
LOGW("couldn't write salt: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t packageNameLen = mPackageName.size();
|
size_t packageNameLen = mPackageName.size();
|
||||||
put4LE(intBuf, packageNameLen);
|
put4LE(intBuf, packageNameLen);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write package name length: %s", strerror(errno));
|
LOGW("couldn't write package name length: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
|
if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
|
||||||
LOGW("couldn't write package name: %s", strerror(errno));
|
LOGW("couldn't write package name: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
put4LE(intBuf, kPackageNameOffset + packageNameLen);
|
put4LE(intBuf, kPackageNameOffset + packageNameLen);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write footer size: %s", strerror(errno));
|
LOGW("couldn't write footer size: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
put4LE(intBuf, kSignature);
|
put4LE(intBuf, kSignature);
|
||||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||||
LOGW("couldn't write footer magic signature: %s", strerror(errno));
|
LOGW("couldn't write footer magic signature: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -63,6 +64,10 @@ TEST_F(ObbFileTest, WriteThenRead) {
|
|||||||
|
|
||||||
mObbFile->setPackageName(String8(packageName));
|
mObbFile->setPackageName(String8(packageName));
|
||||||
mObbFile->setVersion(versionNum);
|
mObbFile->setVersion(versionNum);
|
||||||
|
#define SALT_SIZE 8
|
||||||
|
unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5};
|
||||||
|
EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
|
||||||
|
<< "Salt should be successfully set";
|
||||||
|
|
||||||
EXPECT_TRUE(mObbFile->writeTo(mFileName))
|
EXPECT_TRUE(mObbFile->writeTo(mFileName))
|
||||||
<< "couldn't write to fake .obb file";
|
<< "couldn't write to fake .obb file";
|
||||||
@@ -77,6 +82,19 @@ TEST_F(ObbFileTest, WriteThenRead) {
|
|||||||
const char* currentPackageName = mObbFile->getPackageName().string();
|
const char* currentPackageName = mObbFile->getPackageName().string();
|
||||||
EXPECT_STREQ(packageName, currentPackageName)
|
EXPECT_STREQ(packageName, currentPackageName)
|
||||||
<< "package name didn't come out the same as it went in";
|
<< "package name didn't come out the same as it went in";
|
||||||
|
|
||||||
|
size_t saltLen;
|
||||||
|
const unsigned char* newSalt = mObbFile->getSalt(&saltLen);
|
||||||
|
|
||||||
|
EXPECT_EQ(sizeof(salt), saltLen)
|
||||||
|
<< "salt sizes were not the same";
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(salt); i++) {
|
||||||
|
EXPECT_EQ(salt[i], newSalt[i])
|
||||||
|
<< "salt character " << i << " should be equal";
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0)
|
||||||
|
<< "salts should be the same";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
import com.android.internal.app.IMediaContainerService;
|
import com.android.internal.app.IMediaContainerService;
|
||||||
import com.android.internal.util.HexDump;
|
|
||||||
import com.android.server.am.ActivityManagerService;
|
import com.android.server.am.ActivityManagerService;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -46,13 +45,15 @@ import android.os.storage.IMountShutdownObserver;
|
|||||||
import android.os.storage.IObbActionListener;
|
import android.os.storage.IObbActionListener;
|
||||||
import android.os.storage.OnObbStateChangeListener;
|
import android.os.storage.OnObbStateChangeListener;
|
||||||
import android.os.storage.StorageResultCode;
|
import android.os.storage.StorageResultCode;
|
||||||
import android.security.MessageDigest;
|
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -62,6 +63,10 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MountService implements back-end services for platform storage
|
* MountService implements back-end services for platform storage
|
||||||
* management.
|
* management.
|
||||||
@@ -153,6 +158,18 @@ class MountService extends IMountService.Stub
|
|||||||
*/
|
*/
|
||||||
final private HashSet<String> mAsecMountSet = new HashSet<String>();
|
final private HashSet<String> mAsecMountSet = new HashSet<String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the crypto algorithm key in bits for OBB files. Currently
|
||||||
|
* Twofish is used which takes 128-bit keys.
|
||||||
|
*/
|
||||||
|
private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of times to run SHA1 in the PBKDF2 function for OBB files.
|
||||||
|
* 1024 is reasonably secure and not too slow.
|
||||||
|
*/
|
||||||
|
private static final int PBKDF2_HASH_ROUNDS = 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mounted OBB tracking information. Used to track the current state of all
|
* Mounted OBB tracking information. Used to track the current state of all
|
||||||
* OBBs.
|
* OBBs.
|
||||||
@@ -1901,16 +1918,23 @@ class MountService extends IMountService.Stub
|
|||||||
if (mKey == null) {
|
if (mKey == null) {
|
||||||
hashedKey = "none";
|
hashedKey = "none";
|
||||||
} else {
|
} else {
|
||||||
final MessageDigest md;
|
|
||||||
try {
|
try {
|
||||||
md = MessageDigest.getInstance("MD5");
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||||
|
|
||||||
|
KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt,
|
||||||
|
PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
|
||||||
|
SecretKey key = factory.generateSecret(ks);
|
||||||
|
BigInteger bi = new BigInteger(key.getEncoded());
|
||||||
|
hashedKey = bi.toString(16);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
Slog.e(TAG, "Could not load MD5 algorithm", e);
|
Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
|
||||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
|
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
|
||||||
|
return;
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
|
||||||
|
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hashedKey = HexDump.toHexString(md.digest(mKey.getBytes()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc = StorageResultCode.OperationSucceeded;
|
int rc = StorageResultCode.OperationSucceeded;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
Main.cpp
|
Main.cpp
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -Wall -Werror
|
||||||
|
|
||||||
#LOCAL_C_INCLUDES +=
|
#LOCAL_C_INCLUDES +=
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
@@ -27,4 +29,18 @@ LOCAL_MODULE := obbtool
|
|||||||
|
|
||||||
include $(BUILD_HOST_EXECUTABLE)
|
include $(BUILD_HOST_EXECUTABLE)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := pbkdf2gen
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -Wall -Werror
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := pbkdf2gen.cpp
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES := libcrypto
|
||||||
|
|
||||||
|
include $(BUILD_HOST_EXECUTABLE)
|
||||||
|
|
||||||
endif # TARGET_BUILD_APPS
|
endif # TARGET_BUILD_APPS
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
using namespace android;
|
using namespace android;
|
||||||
|
|
||||||
@@ -29,7 +30,9 @@ static const char* gProgVersion = "1.0";
|
|||||||
static int wantUsage = 0;
|
static int wantUsage = 0;
|
||||||
static int wantVersion = 0;
|
static int wantVersion = 0;
|
||||||
|
|
||||||
#define ADD_OPTS "n:v:o"
|
#define SALT_LEN 8
|
||||||
|
|
||||||
|
#define ADD_OPTS "n:v:os:"
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
{"help", no_argument, &wantUsage, 1},
|
{"help", no_argument, &wantUsage, 1},
|
||||||
{"version", no_argument, &wantVersion, 1},
|
{"version", no_argument, &wantVersion, 1},
|
||||||
@@ -38,14 +41,27 @@ static const struct option longopts[] = {
|
|||||||
{"name", required_argument, NULL, 'n'},
|
{"name", required_argument, NULL, 'n'},
|
||||||
{"version", required_argument, NULL, 'v'},
|
{"version", required_argument, NULL, 'v'},
|
||||||
{"overlay", optional_argument, NULL, 'o'},
|
{"overlay", optional_argument, NULL, 'o'},
|
||||||
|
{"salt", required_argument, NULL, 's'},
|
||||||
|
|
||||||
{NULL, 0, NULL, '\0'}
|
{NULL, 0, NULL, '\0'}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct package_info_t {
|
class PackageInfo {
|
||||||
|
public:
|
||||||
|
PackageInfo()
|
||||||
|
: packageName(NULL)
|
||||||
|
, packageVersion(-1)
|
||||||
|
, overlay(false)
|
||||||
|
, salted(false)
|
||||||
|
{
|
||||||
|
memset(&salt, 0, sizeof(salt));
|
||||||
|
}
|
||||||
|
|
||||||
char* packageName;
|
char* packageName;
|
||||||
int packageVersion;
|
int packageVersion;
|
||||||
bool overlay;
|
bool overlay;
|
||||||
|
bool salted;
|
||||||
|
unsigned char salt[SALT_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -58,6 +74,13 @@ void usage(void)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" %s a[dd] [ OPTIONS ] FILENAME\n"
|
" %s a[dd] [ OPTIONS ] FILENAME\n"
|
||||||
" Adds an OBB signature to the file.\n\n", gProgName);
|
" Adds an OBB signature to the file.\n\n", gProgName);
|
||||||
|
fprintf(stderr,
|
||||||
|
" Options:\n"
|
||||||
|
" -n <package name> sets the OBB package name (required)\n"
|
||||||
|
" -v <OBB version> sets the OBB version (required)\n"
|
||||||
|
" -o sets the OBB overlay flag\n"
|
||||||
|
" -s <8 byte hex salt> sets the crypto key salt (if encrypted)\n"
|
||||||
|
"\n");
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
" %s r[emove] FILENAME\n"
|
" %s r[emove] FILENAME\n"
|
||||||
" Removes the OBB signature from the file.\n\n", gProgName);
|
" Removes the OBB signature from the file.\n\n", gProgName);
|
||||||
@@ -66,7 +89,7 @@ void usage(void)
|
|||||||
" Prints the OBB signature information of a file.\n\n", gProgName);
|
" Prints the OBB signature information of a file.\n\n", gProgName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void doAdd(const char* filename, struct package_info_t* info) {
|
void doAdd(const char* filename, struct PackageInfo* info) {
|
||||||
ObbFile *obb = new ObbFile();
|
ObbFile *obb = new ObbFile();
|
||||||
if (obb->readFrom(filename)) {
|
if (obb->readFrom(filename)) {
|
||||||
fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
|
fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
|
||||||
@@ -76,6 +99,9 @@ void doAdd(const char* filename, struct package_info_t* info) {
|
|||||||
obb->setPackageName(String8(info->packageName));
|
obb->setPackageName(String8(info->packageName));
|
||||||
obb->setVersion(info->packageVersion);
|
obb->setVersion(info->packageVersion);
|
||||||
obb->setOverlay(info->overlay);
|
obb->setOverlay(info->overlay);
|
||||||
|
if (info->salted) {
|
||||||
|
obb->setSalt(info->salt, SALT_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
if (!obb->writeTo(filename)) {
|
if (!obb->writeTo(filename)) {
|
||||||
fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
|
fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
|
||||||
@@ -113,6 +139,40 @@ void doInfo(const char* filename) {
|
|||||||
printf(" Version: %d\n", obb->getVersion());
|
printf(" Version: %d\n", obb->getVersion());
|
||||||
printf(" Flags: 0x%08x\n", obb->getFlags());
|
printf(" Flags: 0x%08x\n", obb->getFlags());
|
||||||
printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false");
|
printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false");
|
||||||
|
printf(" Salt: ");
|
||||||
|
|
||||||
|
size_t saltLen;
|
||||||
|
const unsigned char* salt = obb->getSalt(&saltLen);
|
||||||
|
if (salt != NULL) {
|
||||||
|
for (int i = 0; i < SALT_LEN; i++) {
|
||||||
|
printf("%02x", salt[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
printf("<empty>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromHex(char h, unsigned char *b) {
|
||||||
|
if (h >= '0' && h <= '9') {
|
||||||
|
*b = h - '0';
|
||||||
|
return true;
|
||||||
|
} else if (h >= 'a' && h <= 'f') {
|
||||||
|
*b = h - 'a' + 10;
|
||||||
|
return true;
|
||||||
|
} else if (h >= 'A' && h <= 'F') {
|
||||||
|
*b = h - 'A' + 10;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hexToByte(char h1, char h2, unsigned char* b) {
|
||||||
|
unsigned char first, second;
|
||||||
|
if (!fromHex(h1, &first)) return false;
|
||||||
|
if (!fromHex(h2, &second)) return false;
|
||||||
|
*b = (first << 4) | second;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -120,11 +180,9 @@ void doInfo(const char* filename) {
|
|||||||
*/
|
*/
|
||||||
int main(int argc, char* const argv[])
|
int main(int argc, char* const argv[])
|
||||||
{
|
{
|
||||||
const char *prog = argv[0];
|
|
||||||
struct options *options;
|
|
||||||
int opt;
|
int opt;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
struct package_info_t package_info;
|
struct PackageInfo package_info;
|
||||||
|
|
||||||
int result = 1; // pessimistically assume an error.
|
int result = 1; // pessimistically assume an error.
|
||||||
|
|
||||||
@@ -157,6 +215,25 @@ int main(int argc, char* const argv[])
|
|||||||
case 'o':
|
case 'o':
|
||||||
package_info.overlay = true;
|
package_info.overlay = true;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
if (strlen(optarg) != SALT_LEN * 2) {
|
||||||
|
fprintf(stderr, "ERROR: salt must be 8 bytes in hex (e.g., ABCD65031337D00D)\n\n");
|
||||||
|
wantUsage = 1;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
package_info.salted = true;
|
||||||
|
|
||||||
|
unsigned char b;
|
||||||
|
for (int i = 0, j = 0; i < SALT_LEN; i++, j+=2) {
|
||||||
|
if (!hexToByte(optarg[j], optarg[j+1], &b)) {
|
||||||
|
fprintf(stderr, "ERROR: salt must be in hex (e.g., ABCD65031337D00D)\n");
|
||||||
|
wantUsage = 1;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
package_info.salt[i] = b;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
wantUsage = 1;
|
wantUsage = 1;
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ find_binaries() {
|
|||||||
UMOUNTBIN=`which umount`
|
UMOUNTBIN=`which umount`
|
||||||
DDBIN=`which dd`
|
DDBIN=`which dd`
|
||||||
RSYNCBIN=`which rsync`
|
RSYNCBIN=`which rsync`
|
||||||
|
PBKDF2GEN=`which pbkdf2gen`
|
||||||
}
|
}
|
||||||
|
|
||||||
check_prereqs() {
|
check_prereqs() {
|
||||||
@@ -76,6 +77,11 @@ check_prereqs() {
|
|||||||
echo "ERROR: ${LOSETUPBIN} is not executable!"
|
echo "ERROR: ${LOSETUPBIN} is not executable!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${PBKDF2GEN}x" = "x" ]; then \
|
||||||
|
echo "ERROR: Could not find pbkdf2gen in your path!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
@@ -142,7 +148,6 @@ onexit() {
|
|||||||
usage() {
|
usage() {
|
||||||
echo "mkobb.sh -- Create OBB files for use on Android"
|
echo "mkobb.sh -- Create OBB files for use on Android"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -c Use an encrypted OBB; must specify key"
|
|
||||||
echo " -d <directory> Use <directory> as input for OBB files"
|
echo " -d <directory> Use <directory> as input for OBB files"
|
||||||
echo " -k <key> Use <key> to encrypt OBB file"
|
echo " -k <key> Use <key> to encrypt OBB file"
|
||||||
echo " -K Prompt for key to encrypt OBB file"
|
echo " -K Prompt for key to encrypt OBB file"
|
||||||
@@ -156,7 +161,7 @@ check_prereqs
|
|||||||
|
|
||||||
use_crypto=0
|
use_crypto=0
|
||||||
|
|
||||||
args=`getopt -o cd:hk:Ko:v -- "$@"`
|
args=`getopt -o d:hk:Ko:v -- "$@"`
|
||||||
eval set -- "$args"
|
eval set -- "$args"
|
||||||
|
|
||||||
while true; do \
|
while true; do \
|
||||||
@@ -223,9 +228,9 @@ loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next
|
|||||||
${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
|
${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
|
||||||
|
|
||||||
if [ ${use_crypto} -eq 1 ]; then \
|
if [ ${use_crypto} -eq 1 ]; then \
|
||||||
hashed_key=`echo -n "${key}" | md5sum | awk '{ print $1 }'`
|
eval `${PBKDF2GEN} ${key}`
|
||||||
unique_dm_name=`basename ${tempfile}`
|
unique_dm_name=`basename ${tempfile}`
|
||||||
echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${hashed_key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name}
|
echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name}
|
||||||
old_loop_dev=${loop_dev}
|
old_loop_dev=${loop_dev}
|
||||||
loop_dev=/dev/mapper/${unique_dm_name}
|
loop_dev=/dev/mapper/${unique_dm_name}
|
||||||
fi
|
fi
|
||||||
@@ -253,6 +258,11 @@ echo ""
|
|||||||
|
|
||||||
echo "Successfully created \`${filename}'"
|
echo "Successfully created \`${filename}'"
|
||||||
|
|
||||||
|
if [ ${use_crypto} -eq 1 ]; then \
|
||||||
|
echo "salt for use with obbtool is:"
|
||||||
|
echo "${salt}"
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Undo all the temporaries
|
# Undo all the temporaries
|
||||||
#
|
#
|
||||||
|
|||||||
78
tools/obbtool/pbkdf2gen.cpp
Normal file
78
tools/obbtool/pbkdf2gen.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple program to generate a key based on PBKDF2 with preset inputs.
|
||||||
|
*
|
||||||
|
* Will print out the salt and key in hex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SALT_LEN 8
|
||||||
|
#define ROUNDS 1024
|
||||||
|
#define KEY_BITS 128
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <password>\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Could not open /dev/urandom: %s\n", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char salt[SALT_LEN];
|
||||||
|
|
||||||
|
if (read(fd, &salt, SALT_LEN) != SALT_LEN) {
|
||||||
|
fprintf(stderr, "Could not read salt from /dev/urandom: %s\n", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
unsigned char rawKey[KEY_BITS];
|
||||||
|
|
||||||
|
if (PKCS5_PBKDF2_HMAC_SHA1(argv[1], strlen(argv[1]), salt, SALT_LEN,
|
||||||
|
ROUNDS, KEY_BITS, rawKey) != 1) {
|
||||||
|
fprintf(stderr, "Could not generate PBKDF2 output: %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("salt=");
|
||||||
|
for (int i = 0; i < SALT_LEN; i++) {
|
||||||
|
printf("%02x", salt[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("key=");
|
||||||
|
for (int i = 0; i < (KEY_BITS / 8); i++) {
|
||||||
|
printf("%02x", rawKey[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user