/* * 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 #include "rabbitsign.h" #include "internal.h" #include "md5.h" /* * Check/fix app/OS header and data. * * (This is something of a work in progress; a lot more * experimentation would be useful to determine what exactly is * required of app and OS headers on the 68k calculators.) */ static int repair_app(RSProgram* app, /* app to repair */ unsigned int flags, /* flags */ unsigned int type) /* field type */ { unsigned long length, hdrstart, hdrsize, fieldhead, fieldstart, fieldsize; unsigned char *hdr; int e; if (app->length < 6 || app->data[0] != type || (app->data[1] & 0xf0) != 0) { rs_error(NULL, app, "no app header found"); return RS_ERR_MISSING_HEADER; } /* Determine application length */ length = app->length; rs_get_field_size(app->data, &hdrstart, &hdrsize); /* If requested, remove the old signature (truncate the application to its stated length.) */ if (flags & RS_REMOVE_OLD_SIGNATURE) { if (length < hdrstart + hdrsize) { rs_warning(NULL, app, "provided app data too short"); } else { if (length > hdrstart + hdrsize + 67) rs_warning(NULL, app, "re-signing discards %lu bytes", length - hdrstart - hdrsize); length = hdrstart + hdrsize; } } else if (hdrsize && hdrstart + hdrsize != length) { rs_warning(NULL, app, "application length incorrect"); rs_warning(NULL, app, "(perhaps you meant to use -r?)"); } if ((e = rs_program_set_length(app, length))) return e; /* Set app size header to the correct value */ hdrsize = length - hdrstart; if (rs_set_field_size(app->data, hdrsize)) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "cannot set application length"); else { rs_error(NULL, app, "cannot set application length"); return RS_ERR_FIELD_TOO_SMALL; } } /* Check for key ID */ hdr = app->data + hdrstart; if (hdrsize > 128) hdrsize = 128; if (rs_find_app_field((type << 8) | 0x10, hdr, hdrsize, NULL, NULL, NULL)) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application has no key ID"); else { rs_error(NULL, app, "application has no key ID"); return RS_ERR_MISSING_KEY_ID; } } /* Check for date stamp (note: I haven't actually tested whether this is required, but it always seems to be present in both 68k apps and OSes, and it is required for TI-83+ apps) */ if (rs_find_app_field(0x0320, hdr, hdrsize, NULL, &fieldstart, &fieldsize)) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application has no date stamp"); else { rs_error(NULL, app, "application has no date stamp"); return RS_ERR_MISSING_DATE_STAMP; } } else if (rs_find_app_field(0x0900, hdr + fieldstart, fieldsize, NULL, NULL, NULL)) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application has no date stamp"); else { rs_error(NULL, app, "application has no date stamp"); return RS_ERR_MISSING_DATE_STAMP; } } else if (hdr[fieldstart + fieldsize] != 0x02 || (hdr[fieldstart + fieldsize + 1] & 0xf0) != 0) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application has no date stamp signature"); else { rs_error(NULL, app, "application has no date stamp signature"); return RS_ERR_MISSING_DATE_STAMP; } } /* Check for program image field and fix length */ if (rs_find_app_field((type << 8) | 0x70, hdr, hdrsize, &fieldhead, &fieldstart, &fieldsize)) { if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application has no program image field"); else { rs_error(NULL, app, "application has no program image field"); return RS_ERR_MISSING_PROGRAM_IMAGE; } } else { if ((fieldstart + hdrstart) % 2) { /* The OS appears to align apps so the start of the app header is at an even address; if the application code itself is at an odd address, bad stuff will happen. */ if (flags & RS_IGNORE_ALL_WARNINGS) rs_warning(NULL, app, "application header is not a multiple of 2 bytes"); else { rs_error(NULL, app, "application header is not a multiple of 2 bytes"); return RS_ERR_MISALIGNED_PROGRAM_IMAGE; } } if (fieldsize && fieldstart + fieldsize != length - hdrstart) rs_warning(NULL, app, "program image length incorrect"); if (rs_set_field_size(hdr + fieldhead, length - hdrstart - fieldstart)) { rs_error(NULL, app, "cannot set program image length"); return RS_ERR_FIELD_TOO_SMALL; } } return RS_SUCCESS; } /* * Check/fix Flash app header and data. */ int rs_repair_ti9x_app(RSProgram* app, /* app to repair */ unsigned int flags) /* flags */ { return repair_app(app, flags, 0x81); } /* * Check/fix OS header and data. */ int rs_repair_ti9x_os(RSProgram* app, /* app to repair */ unsigned int flags) /* flags */ { return repair_app(app, flags, 0x80); } /* * Compute signature for a 68k app/OS. * * The app header should be checked and/or repaired by * rs_repair_ti9x_app() prior to calling this function. */ int rs_sign_ti9x_app(RSProgram* app, /* app to sign */ RSKey* key) /* signing key */ { md5_uint32 hash[4]; mpz_t hashv, sigv; unsigned char sigdata[512]; size_t siglength; int e; md5_buffer((char*) app->data, app->length, &hash); mpz_init(hashv); mpz_init(sigv); mpz_import(hashv, 16, -1, 1, 0, 0, hash); rs_message(2, NULL, app, "hash = %ZX", hashv); if ((e = rs_sign_rsa(sigv, hashv, key))) { mpz_clear(hashv); mpz_clear(sigv); return e; } rs_message(2, NULL, app, "sig = %ZX", sigv); sigdata[0] = 0x02; sigdata[1] = 0x0d; mpz_export(sigdata + 3, &siglength, -1, 1, 0, 0, sigv); sigdata[2] = siglength & 0xff; siglength += 3; return rs_program_append_data(app, sigdata, siglength); } /* * Validate app/OS signature. */ int rs_validate_ti9x_app(const RSProgram* app, /* app to validate */ const RSKey* key) /* signing key */ { unsigned long length, hdrstart, hdrsize, fieldstart, fieldsize; const unsigned char *hdr, *sig; md5_uint32 hash[4]; mpz_t hashv, sigv; int e, e2 = RS_SUCCESS; if (app->length < 6) { rs_error(NULL, app, "no app header found"); return RS_ERR_MISSING_HEADER; } rs_get_field_size(app->data, &hdrstart, &hdrsize); length = hdrstart + hdrsize; hdr = app->data + hdrstart; if (hdrsize > 128) hdrsize = 128; if (length + 4 > app->length || length + 67 < app->length) { rs_error(NULL, app, "incorrect application length"); return RS_ERR_INCORRECT_PROGRAM_SIZE; } if (rs_find_app_field((app->data[0] << 8) | 0x70, hdr, hdrsize, NULL, &fieldstart, &fieldsize)) { rs_warning(NULL, app, "application has no program image field"); e2 = RS_ERR_MISSING_PROGRAM_IMAGE; } else if ((fieldstart + hdrstart) % 2) { rs_warning(NULL, app, "application header is not a multiple of 2 bytes"); e2 = RS_ERR_MISALIGNED_PROGRAM_IMAGE; } md5_buffer((char*) app->data, length, &hash); sig = app->data + length; if (sig[0] != 0x02 || (sig[1] & 0xf0) != 0x00) { rs_error(NULL, app, "application does not have an RSA signature"); return RS_ERR_MISSING_RSA_SIGNATURE; } rs_get_field_size(sig, &fieldstart, &fieldsize); mpz_init(sigv); mpz_init(hashv); mpz_import(hashv, 16, -1, 1, 0, 0, hash); rs_message(2, NULL, app, "hash = %ZX", hashv); mpz_import(sigv, fieldsize, -1, 1, 0, 0, sig + fieldstart); rs_message(2, NULL, app, "sig = %ZX", sigv); e = rs_validate_rsa(sigv, hashv, key); if (e == RS_SIGNATURE_INCORRECT) rs_message(0, NULL, app, "application signature incorrect"); mpz_clear(sigv); mpz_clear(hashv); return (e ? e : e2); }