diff options
author | Adam Lesinski <adamlesinski@google.com> | 2014-01-23 18:17:42 -0800 |
---|---|---|
committer | Adam Lesinski <adamlesinski@google.com> | 2014-01-27 10:31:04 -0800 |
commit | 282e181b58cf72b6ca770dc7ca5f91f135444502 (patch) | |
tree | e313e7ab30ff4679562efa37bde29cfcb9e375d3 /tools/obbtool | |
parent | 7023df08f14ec5dee76ac54c03e870f84e297636 (diff) | |
download | frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.zip frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.tar.gz frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.tar.bz2 |
Revert "Move frameworks/base/tools/ to frameworks/tools/"
This reverts commit 9f6a119c8aa276432ece4fe2118bd8a3c9b1067e.
Diffstat (limited to 'tools/obbtool')
-rw-r--r-- | tools/obbtool/Android.mk | 48 | ||||
-rw-r--r-- | tools/obbtool/Main.cpp | 301 | ||||
-rwxr-xr-x | tools/obbtool/mkobb.sh | 281 | ||||
-rw-r--r-- | tools/obbtool/pbkdf2gen.cpp | 78 |
4 files changed, 708 insertions, 0 deletions
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk new file mode 100644 index 0000000..9ff56d6 --- /dev/null +++ b/tools/obbtool/Android.mk @@ -0,0 +1,48 @@ +# +# Copyright 2010 The Android Open Source Project +# +# Opaque Binary Blob (OBB) Tool +# + +# This tool is prebuilt if we're doing an app-only build. +ifeq ($(TARGET_BUILD_APPS),) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + Main.cpp + +LOCAL_CFLAGS := -Wall -Werror + +#LOCAL_C_INCLUDES += + +LOCAL_STATIC_LIBRARIES := \ + libandroidfw \ + libutils \ + libcutils \ + liblog + +ifeq ($(HOST_OS),linux) +LOCAL_LDLIBS += -ldl -lpthread +endif + +LOCAL_MODULE := obbtool + +include $(BUILD_HOST_EXECUTABLE) + +##################################################### +include $(CLEAR_VARS) + +LOCAL_MODULE := pbkdf2gen +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wall -Werror +LOCAL_SRC_FILES := pbkdf2gen.cpp +LOCAL_LDLIBS += -ldl +LOCAL_C_INCLUDES := external/openssl/include $(LOCAL_C_INCLUDES) +LOCAL_STATIC_LIBRARIES := libcrypto_static + +include $(BUILD_HOST_EXECUTABLE) + +####################################################### +endif # TARGET_BUILD_APPS diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp new file mode 100644 index 0000000..b2152e8 --- /dev/null +++ b/tools/obbtool/Main.cpp @@ -0,0 +1,301 @@ +/* + * 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 <androidfw/ObbFile.h> +#include <utils/String8.h> + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +using namespace android; + +static const char* gProgName = "obbtool"; +static const char* gProgVersion = "1.0"; + +static int wantUsage = 0; +static int wantVersion = 0; + +#define SALT_LEN 8 + +#define ADD_OPTS "n:v:os:" +static const struct option longopts[] = { + {"help", no_argument, &wantUsage, 1}, + {"version", no_argument, &wantVersion, 1}, + + /* Args for "add" */ + {"name", required_argument, NULL, 'n'}, + {"version", required_argument, NULL, 'v'}, + {"overlay", optional_argument, NULL, 'o'}, + {"salt", required_argument, NULL, 's'}, + + {NULL, 0, NULL, '\0'} +}; + +class PackageInfo { +public: + PackageInfo() + : packageName(NULL) + , packageVersion(-1) + , overlay(false) + , salted(false) + { + memset(&salt, 0, sizeof(salt)); + } + + char* packageName; + int packageVersion; + bool overlay; + bool salted; + unsigned char salt[SALT_LEN]; +}; + +/* + * Print usage info. + */ +void usage(void) +{ + fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, + " %s a[dd] [ OPTIONS ] FILENAME\n" + " 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, + " %s r[emove] FILENAME\n" + " Removes the OBB signature from the file.\n\n", gProgName); + fprintf(stderr, + " %s i[nfo] FILENAME\n" + " Prints the OBB signature information of a file.\n\n", gProgName); +} + +void doAdd(const char* filename, struct PackageInfo* info) { + ObbFile *obb = new ObbFile(); + if (obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename); + return; + } + + obb->setPackageName(String8(info->packageName)); + obb->setVersion(info->packageVersion); + obb->setOverlay(info->overlay); + if (info->salted) { + obb->setSalt(info->salt, SALT_LEN); + } + + if (!obb->writeTo(filename)) { + fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n", + filename, strerror(errno)); + return; + } + + fprintf(stderr, "OBB signature successfully written\n"); +} + +void doRemove(const char* filename) { + ObbFile *obb = new ObbFile(); + if (!obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename); + return; + } + + if (!obb->removeFrom(filename)) { + fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename); + return; + } + + fprintf(stderr, "OBB signature successfully removed\n"); +} + +void doInfo(const char* filename) { + ObbFile *obb = new ObbFile(); + if (!obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename); + return; + } + + printf("OBB info for '%s':\n", filename); + printf("Package name: %s\n", obb->getPackageName().string()); + printf(" Version: %d\n", obb->getVersion()); + printf(" Flags: 0x%08x\n", obb->getFlags()); + 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; +} + +/* + * Parse args. + */ +int main(int argc, char* const argv[]) +{ + int opt; + int option_index = 0; + struct PackageInfo package_info; + + int result = 1; // pessimistically assume an error. + + if (argc < 2) { + wantUsage = 1; + goto bail; + } + + while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) { + switch (opt) { + case 0: + if (longopts[option_index].flag) + break; + fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name); + wantUsage = 1; + goto bail; + case 'n': + package_info.packageName = optarg; + break; + case 'v': { + char* end; + package_info.packageVersion = strtol(optarg, &end, 10); + if (*optarg == '\0' || *end != '\0') { + fprintf(stderr, "ERROR: invalid version; should be integer!\n\n"); + wantUsage = 1; + goto bail; + } + break; + } + case 'o': + package_info.overlay = true; + 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 '?': + wantUsage = 1; + goto bail; + } + } + + if (wantVersion) { + fprintf(stderr, "%s %s\n", gProgName, gProgVersion); + } + + if (wantUsage) { + goto bail; + } + +#define CHECK_OP(name) \ + if (strncmp(op, name, opsize)) { \ + fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \ + wantUsage = 1; \ + goto bail; \ + } + + if (optind < argc) { + const char* op = argv[optind++]; + const int opsize = strlen(op); + + if (optind >= argc) { + fprintf(stderr, "ERROR: filename required!\n\n"); + wantUsage = 1; + goto bail; + } + + const char* filename = argv[optind++]; + + switch (op[0]) { + case 'a': + CHECK_OP("add"); + if (package_info.packageName == NULL) { + fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n"); + goto bail; + } + doAdd(filename, &package_info); + break; + case 'r': + CHECK_OP("remove"); + doRemove(filename); + break; + case 'i': + CHECK_OP("info"); + doInfo(filename); + break; + default: + fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op); + wantUsage = 1; + goto bail; + } + } + +bail: + if (wantUsage) { + usage(); + result = 2; + } + + return result; +} diff --git a/tools/obbtool/mkobb.sh b/tools/obbtool/mkobb.sh new file mode 100755 index 0000000..725250d --- /dev/null +++ b/tools/obbtool/mkobb.sh @@ -0,0 +1,281 @@ +#!/bin/bash +# +# 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. +# + +# mkobb.sh - Creates OBB files on Linux machines + +# Directory where we should temporarily mount the OBB loopback to copy files +MOUNTDIR=/tmp + +# Presets. Changing these will probably break your OBB on the device +CRYPTO=twofish +FS=vfat +MKFS=mkfs.vfat +LOSETUP=losetup +BLOCK_SIZE=512 +SLOP=512 # Amount of filesystem slop in ${BLOCK_SIZE} blocks + +find_binaries() { + MKFSBIN=`which ${MKFS}` + LOSETUPBIN=`which ${LOSETUP}` + MOUNTBIN=`which mount` + UMOUNTBIN=`which umount` + DDBIN=`which dd` + RSYNCBIN=`which rsync` + PBKDF2GEN=`which pbkdf2gen` +} + +check_prereqs() { + if [ "`uname -s`x" != "Linuxx" ]; then \ + echo "ERROR: This script only works on Linux!" + exit 1 + fi + + if ! egrep -q "^cryptoloop " /proc/modules; then \ + echo "ERROR: Could not find cryptoloop in the kernel." + echo "Perhaps you need to: modprobe cryptoloop" + exit 1 + fi + + if ! egrep -q "name\s*:\s*${CRYPTO}$" /proc/crypto; then \ + echo "ERROR: Could not find crypto \`${CRYPTO}' in the kernel." + echo "Perhaps you need to: modprobe ${CRYPTO}" + exit 1 + fi + + if ! egrep -q "^\s*${FS}$" /proc/filesystems; then \ + echo "ERROR: Could not find filesystem \`${FS}' in the kernel." + echo "Perhaps you need to: modprobe ${FS}" + exit 1 + fi + + if [ "${MKFSBIN}x" = "x" ]; then \ + echo "ERROR: Could not find ${MKFS} in your path!" + exit 1 + elif [ ! -x "${MKFSBIN}" ]; then \ + echo "ERROR: ${MKFSBIN} is not executable!" + exit 1 + fi + + if [ "${LOSETUPBIN}x" = "x" ]; then \ + echo "ERROR: Could not find ${LOSETUP} in your path!" + exit 1 + elif [ ! -x "${LOSETUPBIN}" ]; then \ + echo "ERROR: ${LOSETUPBIN} is not executable!" + exit 1 + fi + + if [ "${PBKDF2GEN}x" = "x" ]; then \ + echo "ERROR: Could not find pbkdf2gen in your path!" + exit 1 + fi +} + +cleanup() { + if [ "${loopdev}x" != "x" ]; then \ + ${LOSETUPBIN} -d ${loopdev} + fi +} + +hidden_prompt() { + unset output + prompt="$1" + outvar="$2" + while read -s -n 1 -p "$prompt" c; do \ + if [ "x$c" = "x" ]; then \ + break + fi + prompt='*' + output="${output}${c}" + done + echo + eval $outvar="$output" + unset output +} + +read_key() { + hidden_prompt " Encryption key: " key + + if [ "${key}x" = "x" ]; then \ + echo "ERROR: An empty key is not allowed!" + exit 1 + fi + + hidden_prompt "Encryption key (again): " key2 + + if [ "${key}x" != "${key2}x" ]; then \ + echo "ERROR: Encryption keys do not match!" + exit 1 + fi +} + +onexit() { + if [ "x${temp_mount}" != "x" ]; then \ + ${UMOUNTBIN} ${temp_mount} + rmdir ${temp_mount} + fi + if [ "x${loop_dev}" != "x" ]; then \ + if [ ${use_crypto} -eq 1 ]; then \ + dmsetup remove -f ${loop_dev} + ${LOSETUPBIN} -d ${old_loop_dev} + else \ + ${LOSETUPBIN} -d ${loop_dev} + fi + fi + if [ "x${tempfile}" != "x" -a -f "${tempfile}" ]; then \ + rm -f ${tempfile} + fi + if [ "x${keyfile}" != "x" -a -f "${keyfile}" ]; then \ + rm -f ${keyfile} + fi + echo "Fatal error." + exit 1 +} + +usage() { + echo "mkobb.sh -- Create OBB files for use on Android" + echo "" + echo " -d <directory> Use <directory> as input for OBB files" + echo " -k <key> Use <key> to encrypt OBB file" + echo " -K Prompt for key to encrypt OBB file" + echo " -o <filename> Write OBB file out to <filename>" + echo " -v Verbose mode" + echo " -h Help; this usage screen" +} + +find_binaries +check_prereqs + +use_crypto=0 + +args=`getopt -o d:hk:Ko:v -- "$@"` +eval set -- "$args" + +while true; do \ + case "$1" in + -d) directory=$2; shift 2;; + -h) usage; exit 1;; + -k) key=$2; use_crypto=1; shift 2;; + -K) prompt_key=1; use_crypto=1; shift;; + -v) verbose=1; shift;; + -o) filename=$2; shift 2;; + --) shift; break;; + *) echo "ERROR: Invalid argument in option parsing! Cannot recover. Ever."; exit 1;; + esac +done + +if [ "${directory}x" = "x" -o ! -d "${directory}" ]; then \ + echo "ERROR: Must specify valid input directory" + echo "" + usage + exit 1; +fi + +if [ "${filename}x" = "x" ]; then \ + echo "ERROR: Must specify filename" + echo "" + usage + exit 1; +fi + +if [ ${use_crypto} -eq 1 -a "${key}x" = "x" -a 0${prompt_key} -eq 0 ]; then \ + echo "ERROR: Crypto desired, but no key supplied or requested to prompt for." + exit 1 +fi + +if [ 0${prompt_key} -eq 1 ]; then \ + read_key +fi + +outdir=`dirname ${filename}` +if [ ! -d "${outdir}" ]; then \ + echo "ERROR: Output directory does not exist: ${outdir}" + exit 1 +fi + +# Make sure we clean up any stuff we create from here on during error conditions +trap onexit ERR + +tempfile=$(tempfile -d ${outdir}) || ( echo "ERROR: couldn't create temporary file in ${outdir}"; exit 1 ) + +block_count=`du -s --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'` +if [ $? -ne 0 ]; then \ + echo "ERROR: Couldn't read size of input directory ${directory}" + exit 1 +fi + +echo "Creating temporary file..." +${DDBIN} if=/dev/zero of=${tempfile} bs=${BLOCK_SIZE} count=$((${block_count} + ${SLOP})) > /dev/null 2>&1 +if [ $? -ne 0 ]; then \ + echo "ERROR: creating temporary file: $?" +fi + +loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next unused device"; exit 1 ) + +${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 ) + +if [ ${use_crypto} -eq 1 ]; then \ + eval `${PBKDF2GEN} ${key}` + unique_dm_name=`basename ${tempfile}` + echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name} + old_loop_dev=${loop_dev} + loop_dev=/dev/mapper/${unique_dm_name} +fi + +# +# Create the filesystem +# +echo "" +${MKFSBIN} -I ${loop_dev} +echo "" + +# +# Make the temporary mount point and mount it +# +temp_mount="${MOUNTDIR}/${RANDOM}" +mkdir ${temp_mount} +${MOUNTBIN} -t ${FS} -o loop ${loop_dev} ${temp_mount} + +# +# rsync the files! +# +echo "Copying files:" +${RSYNCBIN} -av --no-owner --no-group ${directory}/ ${temp_mount}/ +echo "" + +echo "Successfully created \`${filename}'" + +if [ ${use_crypto} -eq 1 ]; then \ + echo "salt for use with obbtool is:" + echo "${salt}" +fi + +# +# Undo all the temporaries +# +umount ${temp_mount} +rmdir ${temp_mount} +if [ ${use_crypto} -eq 1 ]; then \ + dmsetup remove -f ${loop_dev} + ${LOSETUPBIN} -d ${old_loop_dev} +else \ + ${LOSETUPBIN} -d ${loop_dev} +fi +mv ${tempfile} ${filename} + +trap - ERR + +exit 0 diff --git a/tools/obbtool/pbkdf2gen.cpp b/tools/obbtool/pbkdf2gen.cpp new file mode 100644 index 0000000..98d67c0 --- /dev/null +++ b/tools/obbtool/pbkdf2gen.cpp @@ -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"); +} |