/*
* RabbitSign - Tools for signing TI graphing calculator software
* Copyright (C) 2009 Benjamin Moody
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#ifdef HAVE_STDLIB_H
# include
#endif
#ifdef HAVE_STRING_H
# include
#else
# ifdef HAVE_STRINGS_H
# include
# endif
#endif
#include "rabbitsign.h"
#include "internal.h"
#if !defined(strcasecmp) && !defined(HAVE_STRCASECMP)
# ifdef HAVE_STRICMP
# define strcasecmp stricmp
# else
# define strcasecmp strcmp
# endif
#endif
#if !defined(strrchr) && !defined(HAVE_STRRCHR) && defined(HAVE_RINDEX)
# define strrchr rindex
#endif
static const char* getbasename(const char* f)
{
const char *p;
if ((p = strrchr(f, '/')))
f = p + 1;
#if defined(__MSDOS__) || defined(__WIN32__)
if ((p = strrchr(f, '\\')))
f = p + 1;
#endif
return f;
}
static const char* usage[]={
"Usage: %s [options] app-file ...\n",
"Where options may include:\n",
" -a: appsign compatibility mode (Unix-style output)\n",
" -b: read raw binary data (default: auto-detect)\n",
" -c: check existing app signatures rather than signing\n",
" -f: force signing despite errors\n",
" -g: write app in GraphLink (XXk) format\n",
" -k KEYFILE: use specified key file\n",
" -K NUM: use specified key ID (hexadecimal)\n",
" -n: do not alter the app header\n",
" -o OUTFILE: write to specified output file (default is .app\n",
" or .8xk)\n",
" -p: fix the pages field if found\n",
" -P: add an extra page if necessary\n",
" -q: suppress warning messages\n",
" -r: re-sign a previously signed app (i.e. strip off all\n",
" data beyond that indicated by the size header)\n",
" -R R: specify the root to use (0, 1, 2, or 3) (default is 0)\n",
" -t TYPE: specify program type (e.g. 8xk, 73u)\n",
" -u: assume plain hex input is unsorted (default is sorted)\n",
" -v: be verbose (-vv for even more verbosity)\n",
" --help: describe options\n",
" --version: print version info\n",
NULL};
int main(int argc, char** argv)
{
unsigned int flags = (RS_INPUT_SORTED | RS_OUTPUT_HEX_ONLY);
int rootnum = 0; /* which of the four valid signatures
to generate
0 = standard (r,s)
1 = (-r,s)
2 = (r,-s)
3 = (-r,-s) */
int rawmode = 0; /* 0 = fix app headers
1 = sign "raw" data */
int valmode = 0; /* 0 = sign apps
1 = validate apps */
const char* infilename; /* file name for input */
const char* outfilename = NULL; /* file name for output */
const char* keyfilename = NULL; /* file name for key */
int verbose = 0; /* -1 = quiet (errors only)
0 = default (warnings + errors)
1 = verbose (print file names / status)
2 = very verbose (details of computation) */
static const char optstring[] = "abcfgk:K:no:pPqrR:t:uv";
const char *progname;
int i, j, c, e;
const char* arg;
char *tempname;
FILE* infile;
FILE* outfile;
RSKey* key;
RSProgram* prgm;
unsigned long keyid = 0, appkeyid;
RSCalcType ctype = RS_CALC_UNKNOWN;
RSDataType dtype = RS_DATA_UNKNOWN;
char *ptr;
const char *ext;
int invalidapps = 0;
progname = getbasename(argv[0]);
rs_set_progname(progname);
if (argc == 1) {
fprintf(stderr, usage[0], progname);
for (i = 1; usage[i]; i++)
fputs(usage[i], stderr);
return 5;
}
i = j = 1;
while ((c = rs_parse_cmdline(argc, argv, optstring, &i, &j, &arg))) {
switch (c) {
case RS_CMDLINE_HELP:
printf(usage[0], progname);
for (i = 1; usage[i]; i++)
fputs(usage[i], stdout);
return 0;
case RS_CMDLINE_VERSION:
printf("rabbitsign\n");
fputs("Copyright (C) 2009 Benjamin Moody\n", stdout);
fputs("This program is free software. ", stdout);
fputs("There is NO WARRANTY of any kind.\n", stdout);
return 0;
case 'o':
outfilename = arg;
break;
case 'k':
keyfilename = arg;
break;
case 'K':
if (!sscanf(arg, "%lx", &keyid)) {
fprintf(stderr, "%s: -K: invalid argument %s\n", progname, arg);
return 5;
}
break;
case 'b':
flags |= RS_INPUT_BINARY;
break;
case 'u':
flags &= ~RS_INPUT_SORTED;
break;
case 'f':
flags |= RS_IGNORE_ALL_WARNINGS;
break;
case 'g':
flags &= ~RS_OUTPUT_HEX_ONLY;
break;
case 'a':
flags |= RS_OUTPUT_APPSIGN;
break;
case 'R':
if (!sscanf(arg, "%d", &rootnum)) {
fprintf(stderr, "%s: -R: invalid argument %s\n", progname, arg);
return 5;
}
break;
case 't':
if (rs_suffix_to_type(arg, &ctype, &dtype)) {
fprintf(stderr, "%s: unrecognized file type %s\n", progname, arg);
return 5;
}
break;
case 'n':
rawmode = 1;
break;
case 'r':
flags |= RS_REMOVE_OLD_SIGNATURE;
break;
case 'P':
flags |= RS_ZEALOUSLY_PAD_APP;
break;
case 'p':
flags |= RS_FIX_PAGE_COUNT;
break;
case 'c':
valmode = 1;
break;
case 'v':
verbose++;
break;
case 'q':
verbose--;
break;
case RS_CMDLINE_FILENAME:
break;
case RS_CMDLINE_ERROR:
return 5;
default:
fprintf(stderr, "%s: internal error: unknown option -%c\n",
progname, c);
abort();
}
}
rs_set_verbose(verbose);
if (outfilename && (ptr = strrchr(outfilename, '.'))
&& !rs_suffix_to_type(ptr + 1, NULL, NULL))
flags &= ~RS_OUTPUT_HEX_ONLY;
/* Read key file (if manually specified) */
key = rs_key_new();
if (keyfilename) {
infile = fopen(keyfilename, "rb");
if (!infile) {
perror(keyfilename);
rs_key_free(key);
return 3;
}
if (rs_read_key_file(key, infile, keyfilename, 1)) {
fclose(infile);
rs_key_free(key);
return 3;
}
fclose(infile);
}
else if (keyid) {
if (rs_key_find_for_id(key, keyid, valmode)) {
rs_key_free(key);
return 3;
}
}
/* Process applications */
i = j = 1;
while ((c = rs_parse_cmdline(argc, argv, optstring, &i, &j, &arg))) {
if (c != RS_CMDLINE_FILENAME)
continue;
/* Read input file */
if (strcmp(arg, "-")) {
infilename = arg;
infile = fopen(arg, "rb");
if (!infile) {
perror(arg);
rs_key_free(key);
return 4;
}
}
else {
infilename = "(standard input)";
infile = stdin;
}
prgm = rs_program_new();
if (ctype && dtype) {
prgm->calctype = ctype;
prgm->datatype = dtype;
}
else if ((ptr = strrchr(infilename, '.'))) {
rs_suffix_to_type(ptr + 1, &prgm->calctype, &prgm->datatype);
}
if (rs_read_program_file(prgm, infile, infilename, flags)) {
rs_program_free(prgm);
rs_key_free(key);
if (infile != stdin)
fclose(infile);
return 4;
}
if (infile != stdin)
fclose(infile);
/* Read key file (if automatic) */
if (!keyfilename && !keyid) {
appkeyid = rs_program_get_key_id(prgm);
if (!appkeyid) {
fprintf(stderr, "%s: unable to determine key ID\n", infilename);
rs_program_free(prgm);
rs_key_free(key);
return 3;
}
if (appkeyid != key->id) {
if (rs_key_find_for_id(key, appkeyid, valmode)) {
rs_program_free(prgm);
rs_key_free(key);
return 3;
}
}
}
if (valmode) {
/* Validate application */
if (verbose > 0)
fprintf(stderr, "Validating %s %s %s...\n",
rs_calc_type_to_string(prgm->calctype),
rs_data_type_to_string(prgm->datatype),
infilename);
if (rs_validate_program(prgm, key))
invalidapps++;
}
else {
/* Sign application */
if (verbose > 0)
fprintf(stderr, "Signing %s %s %s...\n",
rs_calc_type_to_string(prgm->calctype),
rs_data_type_to_string(prgm->datatype),
infilename);
if (!rawmode) {
if ((e = rs_repair_program(prgm, flags))) {
if (!(flags & RS_IGNORE_ALL_WARNINGS)
&& e < RS_ERR_CRITICAL)
fprintf(stderr, "(use -f to override)\n");
rs_program_free(prgm);
rs_key_free(key);
return 2;
}
}
if (rs_sign_program(prgm, key, rootnum)) {
rs_program_free(prgm);
rs_key_free(key);
return 2;
}
/* Generate output file name */
if (outfilename) {
if (strcmp(outfilename, "-")) {
outfile = fopen(outfilename, "wb");
if (!outfile) {
perror(outfilename);
rs_program_free(prgm);
rs_key_free(key);
return 4;
}
}
else {
outfile = stdout;
}
}
else if (infile == stdin) {
outfile = stdout;
}
else {
ext = rs_type_to_suffix(prgm->calctype, prgm->datatype,
(flags & RS_OUTPUT_HEX_ONLY));
tempname = rs_malloc(strlen(infilename) + 32);
if (!tempname) {
rs_program_free(prgm);
rs_key_free(key);
return 4;
}
strcpy(tempname, infilename);
ptr = strrchr(tempname, '.');
if (!ptr) {
strcat(tempname, ".");
strcat(tempname, ext);
}
else if (strcasecmp(ptr + 1, ext)) {
strcpy(ptr + 1, ext);
}
else {
strcpy(ptr, "-signed.");
strcat(ptr, ext);
}
outfile = fopen(tempname, "wb");
if (!outfile) {
perror(tempname);
rs_free(tempname);
rs_program_free(prgm);
rs_key_free(key);
return 4;
}
rs_free(tempname);
}
/* Write signed application to output file */
if (rs_write_program_file(prgm, outfile, 0, 0, 0, flags)) {
if (outfile != stdout)
fclose(outfile);
rs_program_free(prgm);
rs_key_free(key);
return 4;
}
if (outfile != stdout)
fclose(outfile);
}
rs_program_free(prgm);
}
rs_key_free(key);
if (invalidapps)
return 1;
else
return 0;
}