1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2010 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include <androidfw/ObbFile.h>
18*d57664e9SAndroid Build Coastguard Worker #include <utils/String8.h>
19*d57664e9SAndroid Build Coastguard Worker
20*d57664e9SAndroid Build Coastguard Worker #include <getopt.h>
21*d57664e9SAndroid Build Coastguard Worker #include <stdio.h>
22*d57664e9SAndroid Build Coastguard Worker #include <stdlib.h>
23*d57664e9SAndroid Build Coastguard Worker #include <string.h>
24*d57664e9SAndroid Build Coastguard Worker
25*d57664e9SAndroid Build Coastguard Worker using namespace android;
26*d57664e9SAndroid Build Coastguard Worker
27*d57664e9SAndroid Build Coastguard Worker static const char* gProgName = "obbtool";
28*d57664e9SAndroid Build Coastguard Worker static const char* gProgVersion = "1.0";
29*d57664e9SAndroid Build Coastguard Worker
30*d57664e9SAndroid Build Coastguard Worker static int wantUsage = 0;
31*d57664e9SAndroid Build Coastguard Worker static int wantVersion = 0;
32*d57664e9SAndroid Build Coastguard Worker
33*d57664e9SAndroid Build Coastguard Worker #define SALT_LEN 8
34*d57664e9SAndroid Build Coastguard Worker
35*d57664e9SAndroid Build Coastguard Worker #define ADD_OPTS "n:v:os:"
36*d57664e9SAndroid Build Coastguard Worker static const struct option longopts[] = {
37*d57664e9SAndroid Build Coastguard Worker {"help", no_argument, &wantUsage, 1},
38*d57664e9SAndroid Build Coastguard Worker {"version", no_argument, &wantVersion, 1},
39*d57664e9SAndroid Build Coastguard Worker
40*d57664e9SAndroid Build Coastguard Worker /* Args for "add" */
41*d57664e9SAndroid Build Coastguard Worker {"name", required_argument, NULL, 'n'},
42*d57664e9SAndroid Build Coastguard Worker {"version", required_argument, NULL, 'v'},
43*d57664e9SAndroid Build Coastguard Worker {"overlay", optional_argument, NULL, 'o'},
44*d57664e9SAndroid Build Coastguard Worker {"salt", required_argument, NULL, 's'},
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker {NULL, 0, NULL, '\0'}
47*d57664e9SAndroid Build Coastguard Worker };
48*d57664e9SAndroid Build Coastguard Worker
49*d57664e9SAndroid Build Coastguard Worker class PackageInfo {
50*d57664e9SAndroid Build Coastguard Worker public:
PackageInfo()51*d57664e9SAndroid Build Coastguard Worker PackageInfo()
52*d57664e9SAndroid Build Coastguard Worker : packageName(NULL)
53*d57664e9SAndroid Build Coastguard Worker , packageVersion(-1)
54*d57664e9SAndroid Build Coastguard Worker , overlay(false)
55*d57664e9SAndroid Build Coastguard Worker , salted(false)
56*d57664e9SAndroid Build Coastguard Worker {
57*d57664e9SAndroid Build Coastguard Worker memset(&salt, 0, sizeof(salt));
58*d57664e9SAndroid Build Coastguard Worker }
59*d57664e9SAndroid Build Coastguard Worker
60*d57664e9SAndroid Build Coastguard Worker char* packageName;
61*d57664e9SAndroid Build Coastguard Worker int packageVersion;
62*d57664e9SAndroid Build Coastguard Worker bool overlay;
63*d57664e9SAndroid Build Coastguard Worker bool salted;
64*d57664e9SAndroid Build Coastguard Worker unsigned char salt[SALT_LEN];
65*d57664e9SAndroid Build Coastguard Worker };
66*d57664e9SAndroid Build Coastguard Worker
67*d57664e9SAndroid Build Coastguard Worker /*
68*d57664e9SAndroid Build Coastguard Worker * Print usage info.
69*d57664e9SAndroid Build Coastguard Worker */
usage(void)70*d57664e9SAndroid Build Coastguard Worker void usage(void)
71*d57664e9SAndroid Build Coastguard Worker {
72*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n");
73*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "Usage:\n");
74*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
75*d57664e9SAndroid Build Coastguard Worker " %s a[dd] [ OPTIONS ] FILENAME\n"
76*d57664e9SAndroid Build Coastguard Worker " Adds an OBB signature to the file.\n\n", gProgName);
77*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
78*d57664e9SAndroid Build Coastguard Worker " Options:\n"
79*d57664e9SAndroid Build Coastguard Worker " -n <package name> sets the OBB package name (required)\n"
80*d57664e9SAndroid Build Coastguard Worker " -v <OBB version> sets the OBB version (required)\n"
81*d57664e9SAndroid Build Coastguard Worker " -o sets the OBB overlay flag\n"
82*d57664e9SAndroid Build Coastguard Worker " -s <8 byte hex salt> sets the crypto key salt (if encrypted)\n"
83*d57664e9SAndroid Build Coastguard Worker "\n");
84*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
85*d57664e9SAndroid Build Coastguard Worker " %s r[emove] FILENAME\n"
86*d57664e9SAndroid Build Coastguard Worker " Removes the OBB signature from the file.\n\n", gProgName);
87*d57664e9SAndroid Build Coastguard Worker fprintf(stderr,
88*d57664e9SAndroid Build Coastguard Worker " %s i[nfo] FILENAME\n"
89*d57664e9SAndroid Build Coastguard Worker " Prints the OBB signature information of a file.\n\n", gProgName);
90*d57664e9SAndroid Build Coastguard Worker }
91*d57664e9SAndroid Build Coastguard Worker
doAdd(const char * filename,PackageInfo * info)92*d57664e9SAndroid Build Coastguard Worker void doAdd(const char* filename, PackageInfo* info) {
93*d57664e9SAndroid Build Coastguard Worker ObbFile *obb = new ObbFile();
94*d57664e9SAndroid Build Coastguard Worker if (obb->readFrom(filename)) {
95*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
96*d57664e9SAndroid Build Coastguard Worker return;
97*d57664e9SAndroid Build Coastguard Worker }
98*d57664e9SAndroid Build Coastguard Worker
99*d57664e9SAndroid Build Coastguard Worker obb->setPackageName(String8(info->packageName));
100*d57664e9SAndroid Build Coastguard Worker obb->setVersion(info->packageVersion);
101*d57664e9SAndroid Build Coastguard Worker obb->setOverlay(info->overlay);
102*d57664e9SAndroid Build Coastguard Worker if (info->salted) {
103*d57664e9SAndroid Build Coastguard Worker obb->setSalt(info->salt, SALT_LEN);
104*d57664e9SAndroid Build Coastguard Worker }
105*d57664e9SAndroid Build Coastguard Worker
106*d57664e9SAndroid Build Coastguard Worker if (!obb->writeTo(filename)) {
107*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
108*d57664e9SAndroid Build Coastguard Worker filename, strerror(errno));
109*d57664e9SAndroid Build Coastguard Worker return;
110*d57664e9SAndroid Build Coastguard Worker }
111*d57664e9SAndroid Build Coastguard Worker
112*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "OBB signature successfully written\n");
113*d57664e9SAndroid Build Coastguard Worker }
114*d57664e9SAndroid Build Coastguard Worker
doRemove(const char * filename)115*d57664e9SAndroid Build Coastguard Worker void doRemove(const char* filename) {
116*d57664e9SAndroid Build Coastguard Worker ObbFile *obb = new ObbFile();
117*d57664e9SAndroid Build Coastguard Worker if (!obb->readFrom(filename)) {
118*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename);
119*d57664e9SAndroid Build Coastguard Worker return;
120*d57664e9SAndroid Build Coastguard Worker }
121*d57664e9SAndroid Build Coastguard Worker
122*d57664e9SAndroid Build Coastguard Worker if (!obb->removeFrom(filename)) {
123*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename);
124*d57664e9SAndroid Build Coastguard Worker return;
125*d57664e9SAndroid Build Coastguard Worker }
126*d57664e9SAndroid Build Coastguard Worker
127*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "OBB signature successfully removed\n");
128*d57664e9SAndroid Build Coastguard Worker }
129*d57664e9SAndroid Build Coastguard Worker
doInfo(const char * filename)130*d57664e9SAndroid Build Coastguard Worker void doInfo(const char* filename) {
131*d57664e9SAndroid Build Coastguard Worker ObbFile *obb = new ObbFile();
132*d57664e9SAndroid Build Coastguard Worker if (!obb->readFrom(filename)) {
133*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename);
134*d57664e9SAndroid Build Coastguard Worker return;
135*d57664e9SAndroid Build Coastguard Worker }
136*d57664e9SAndroid Build Coastguard Worker
137*d57664e9SAndroid Build Coastguard Worker printf("OBB info for '%s':\n", filename);
138*d57664e9SAndroid Build Coastguard Worker printf("Package name: %s\n", obb->getPackageName().c_str());
139*d57664e9SAndroid Build Coastguard Worker printf(" Version: %d\n", obb->getVersion());
140*d57664e9SAndroid Build Coastguard Worker printf(" Flags: 0x%08x\n", obb->getFlags());
141*d57664e9SAndroid Build Coastguard Worker printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false");
142*d57664e9SAndroid Build Coastguard Worker printf(" Salt: ");
143*d57664e9SAndroid Build Coastguard Worker
144*d57664e9SAndroid Build Coastguard Worker size_t saltLen;
145*d57664e9SAndroid Build Coastguard Worker const unsigned char* salt = obb->getSalt(&saltLen);
146*d57664e9SAndroid Build Coastguard Worker if (salt != NULL) {
147*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < SALT_LEN; i++) {
148*d57664e9SAndroid Build Coastguard Worker printf("%02x", salt[i]);
149*d57664e9SAndroid Build Coastguard Worker }
150*d57664e9SAndroid Build Coastguard Worker printf("\n");
151*d57664e9SAndroid Build Coastguard Worker } else {
152*d57664e9SAndroid Build Coastguard Worker printf("<empty>\n");
153*d57664e9SAndroid Build Coastguard Worker }
154*d57664e9SAndroid Build Coastguard Worker }
155*d57664e9SAndroid Build Coastguard Worker
fromHex(char h,unsigned char * b)156*d57664e9SAndroid Build Coastguard Worker bool fromHex(char h, unsigned char *b) {
157*d57664e9SAndroid Build Coastguard Worker if (h >= '0' && h <= '9') {
158*d57664e9SAndroid Build Coastguard Worker *b = h - '0';
159*d57664e9SAndroid Build Coastguard Worker return true;
160*d57664e9SAndroid Build Coastguard Worker } else if (h >= 'a' && h <= 'f') {
161*d57664e9SAndroid Build Coastguard Worker *b = h - 'a' + 10;
162*d57664e9SAndroid Build Coastguard Worker return true;
163*d57664e9SAndroid Build Coastguard Worker } else if (h >= 'A' && h <= 'F') {
164*d57664e9SAndroid Build Coastguard Worker *b = h - 'A' + 10;
165*d57664e9SAndroid Build Coastguard Worker return true;
166*d57664e9SAndroid Build Coastguard Worker }
167*d57664e9SAndroid Build Coastguard Worker return false;
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker
hexToByte(char h1,char h2,unsigned char * b)170*d57664e9SAndroid Build Coastguard Worker bool hexToByte(char h1, char h2, unsigned char* b) {
171*d57664e9SAndroid Build Coastguard Worker unsigned char first, second;
172*d57664e9SAndroid Build Coastguard Worker if (!fromHex(h1, &first)) return false;
173*d57664e9SAndroid Build Coastguard Worker if (!fromHex(h2, &second)) return false;
174*d57664e9SAndroid Build Coastguard Worker *b = (first << 4) | second;
175*d57664e9SAndroid Build Coastguard Worker return true;
176*d57664e9SAndroid Build Coastguard Worker }
177*d57664e9SAndroid Build Coastguard Worker
178*d57664e9SAndroid Build Coastguard Worker /*
179*d57664e9SAndroid Build Coastguard Worker * Parse args.
180*d57664e9SAndroid Build Coastguard Worker */
main(int argc,char * const argv[])181*d57664e9SAndroid Build Coastguard Worker int main(int argc, char* const argv[])
182*d57664e9SAndroid Build Coastguard Worker {
183*d57664e9SAndroid Build Coastguard Worker int opt;
184*d57664e9SAndroid Build Coastguard Worker int option_index = 0;
185*d57664e9SAndroid Build Coastguard Worker PackageInfo package_info;
186*d57664e9SAndroid Build Coastguard Worker
187*d57664e9SAndroid Build Coastguard Worker int result = 1; // pessimistically assume an error.
188*d57664e9SAndroid Build Coastguard Worker
189*d57664e9SAndroid Build Coastguard Worker if (argc < 2) {
190*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
191*d57664e9SAndroid Build Coastguard Worker goto bail;
192*d57664e9SAndroid Build Coastguard Worker }
193*d57664e9SAndroid Build Coastguard Worker
194*d57664e9SAndroid Build Coastguard Worker while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) {
195*d57664e9SAndroid Build Coastguard Worker switch (opt) {
196*d57664e9SAndroid Build Coastguard Worker case 0:
197*d57664e9SAndroid Build Coastguard Worker if (longopts[option_index].flag)
198*d57664e9SAndroid Build Coastguard Worker break;
199*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name);
200*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
201*d57664e9SAndroid Build Coastguard Worker goto bail;
202*d57664e9SAndroid Build Coastguard Worker case 'n':
203*d57664e9SAndroid Build Coastguard Worker package_info.packageName = optarg;
204*d57664e9SAndroid Build Coastguard Worker break;
205*d57664e9SAndroid Build Coastguard Worker case 'v': {
206*d57664e9SAndroid Build Coastguard Worker char* end;
207*d57664e9SAndroid Build Coastguard Worker package_info.packageVersion = strtol(optarg, &end, 10);
208*d57664e9SAndroid Build Coastguard Worker if (*optarg == '\0' || *end != '\0') {
209*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: invalid version; should be integer!\n\n");
210*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
211*d57664e9SAndroid Build Coastguard Worker goto bail;
212*d57664e9SAndroid Build Coastguard Worker }
213*d57664e9SAndroid Build Coastguard Worker break;
214*d57664e9SAndroid Build Coastguard Worker }
215*d57664e9SAndroid Build Coastguard Worker case 'o':
216*d57664e9SAndroid Build Coastguard Worker package_info.overlay = true;
217*d57664e9SAndroid Build Coastguard Worker break;
218*d57664e9SAndroid Build Coastguard Worker case 's':
219*d57664e9SAndroid Build Coastguard Worker if (strlen(optarg) != SALT_LEN * 2) {
220*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: salt must be 8 bytes in hex (e.g., ABCD65031337D00D)\n\n");
221*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
222*d57664e9SAndroid Build Coastguard Worker goto bail;
223*d57664e9SAndroid Build Coastguard Worker }
224*d57664e9SAndroid Build Coastguard Worker
225*d57664e9SAndroid Build Coastguard Worker package_info.salted = true;
226*d57664e9SAndroid Build Coastguard Worker
227*d57664e9SAndroid Build Coastguard Worker unsigned char b;
228*d57664e9SAndroid Build Coastguard Worker for (int i = 0, j = 0; i < SALT_LEN; i++, j+=2) {
229*d57664e9SAndroid Build Coastguard Worker if (!hexToByte(optarg[j], optarg[j+1], &b)) {
230*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: salt must be in hex (e.g., ABCD65031337D00D)\n");
231*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
232*d57664e9SAndroid Build Coastguard Worker goto bail;
233*d57664e9SAndroid Build Coastguard Worker }
234*d57664e9SAndroid Build Coastguard Worker package_info.salt[i] = b;
235*d57664e9SAndroid Build Coastguard Worker }
236*d57664e9SAndroid Build Coastguard Worker break;
237*d57664e9SAndroid Build Coastguard Worker case '?':
238*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
239*d57664e9SAndroid Build Coastguard Worker goto bail;
240*d57664e9SAndroid Build Coastguard Worker }
241*d57664e9SAndroid Build Coastguard Worker }
242*d57664e9SAndroid Build Coastguard Worker
243*d57664e9SAndroid Build Coastguard Worker if (wantVersion) {
244*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "%s %s\n", gProgName, gProgVersion);
245*d57664e9SAndroid Build Coastguard Worker }
246*d57664e9SAndroid Build Coastguard Worker
247*d57664e9SAndroid Build Coastguard Worker if (wantUsage) {
248*d57664e9SAndroid Build Coastguard Worker goto bail;
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker
251*d57664e9SAndroid Build Coastguard Worker #define CHECK_OP(name) \
252*d57664e9SAndroid Build Coastguard Worker if (strncmp(op, name, opsize)) { \
253*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \
254*d57664e9SAndroid Build Coastguard Worker wantUsage = 1; \
255*d57664e9SAndroid Build Coastguard Worker goto bail; \
256*d57664e9SAndroid Build Coastguard Worker }
257*d57664e9SAndroid Build Coastguard Worker
258*d57664e9SAndroid Build Coastguard Worker if (optind < argc) {
259*d57664e9SAndroid Build Coastguard Worker const char* op = argv[optind++];
260*d57664e9SAndroid Build Coastguard Worker const int opsize = strlen(op);
261*d57664e9SAndroid Build Coastguard Worker
262*d57664e9SAndroid Build Coastguard Worker if (optind >= argc) {
263*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: filename required!\n\n");
264*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
265*d57664e9SAndroid Build Coastguard Worker goto bail;
266*d57664e9SAndroid Build Coastguard Worker }
267*d57664e9SAndroid Build Coastguard Worker
268*d57664e9SAndroid Build Coastguard Worker const char* filename = argv[optind++];
269*d57664e9SAndroid Build Coastguard Worker
270*d57664e9SAndroid Build Coastguard Worker switch (op[0]) {
271*d57664e9SAndroid Build Coastguard Worker case 'a':
272*d57664e9SAndroid Build Coastguard Worker CHECK_OP("add");
273*d57664e9SAndroid Build Coastguard Worker if (package_info.packageName == NULL) {
274*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n");
275*d57664e9SAndroid Build Coastguard Worker goto bail;
276*d57664e9SAndroid Build Coastguard Worker }
277*d57664e9SAndroid Build Coastguard Worker doAdd(filename, &package_info);
278*d57664e9SAndroid Build Coastguard Worker break;
279*d57664e9SAndroid Build Coastguard Worker case 'r':
280*d57664e9SAndroid Build Coastguard Worker CHECK_OP("remove");
281*d57664e9SAndroid Build Coastguard Worker doRemove(filename);
282*d57664e9SAndroid Build Coastguard Worker break;
283*d57664e9SAndroid Build Coastguard Worker case 'i':
284*d57664e9SAndroid Build Coastguard Worker CHECK_OP("info");
285*d57664e9SAndroid Build Coastguard Worker doInfo(filename);
286*d57664e9SAndroid Build Coastguard Worker break;
287*d57664e9SAndroid Build Coastguard Worker default:
288*d57664e9SAndroid Build Coastguard Worker fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op);
289*d57664e9SAndroid Build Coastguard Worker wantUsage = 1;
290*d57664e9SAndroid Build Coastguard Worker goto bail;
291*d57664e9SAndroid Build Coastguard Worker }
292*d57664e9SAndroid Build Coastguard Worker }
293*d57664e9SAndroid Build Coastguard Worker
294*d57664e9SAndroid Build Coastguard Worker bail:
295*d57664e9SAndroid Build Coastguard Worker if (wantUsage) {
296*d57664e9SAndroid Build Coastguard Worker usage();
297*d57664e9SAndroid Build Coastguard Worker result = 2;
298*d57664e9SAndroid Build Coastguard Worker }
299*d57664e9SAndroid Build Coastguard Worker
300*d57664e9SAndroid Build Coastguard Worker return result;
301*d57664e9SAndroid Build Coastguard Worker }
302