/*
* 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_STRING_H
# include
#else
# ifdef HAVE_STRINGS_H
# include
# endif
#endif
#include "rabbitsign.h"
#include "internal.h"
/*
* Create a new key.
*/
RSKey* rs_key_new()
{
RSKey* key = rs_malloc(sizeof(RSKey));
if (!key)
return NULL;
key->filename = NULL;
key->id = 0;
mpz_init(key->n);
mpz_init(key->p);
mpz_init(key->q);
mpz_init(key->qinv);
mpz_init(key->d);
return key;
}
/*
* Free a key.
*/
void rs_key_free(RSKey* key)
{
if (!key)
return;
rs_free(key->filename);
mpz_clear(key->n);
mpz_clear(key->p);
mpz_clear(key->q);
mpz_clear(key->qinv);
mpz_clear(key->d);
rs_free(key);
}
/*
* Parse a number written in TI's hexadecimal key format.
*/
static int parse_value(mpz_t dest, /* mpz to store result */
const char* str) /* string to parse */
{
unsigned int count, b, i;
int n;
unsigned char buf[256];
if (1 > sscanf(str, "%2X%n", &count, &n) || n != 2
|| (count * 2 + 2) > strlen(str))
return 1;
for (i = 0; i < count; i++) {
if (1 > sscanf(str + 2 + 2 * i, "%2X%n", &b, &n) || n != 2)
return 1;
buf[i] = b;
}
mpz_import(dest, i, -1, 1, 0, 0, buf);
return 0;
}
/*
* Read key from a file.
*
* Two formats of key file are supported:
*
* "Rabin" style (the type used by the TI-83 Plus SDK) consists of
* three lines: the public key (n) followed by its two factors (p and
* q.)
*
* "RSA" style (the type used by the TI-89/92 Plus SDK) also consists
* of three lines: the key ID, the public key (n), and the signing
* exponent (d).
*
* In either case, if we are only interested in validating signatures,
* the private key may be omitted.
*
* Note that "Rabin" style key files can be used to generate RSA
* signatures, but not vice versa.
*/
int rs_read_key_file(RSKey* key, /* key structure */
FILE* f, /* file to read */
const char* fname, /* file name */
int verify) /* 1 = check key validity */
{
char buf[1024];
mpz_t tmp;
rs_free(key->filename);
key->filename = rs_strdup(fname);
if (fname && !key->filename)
return RS_ERR_OUT_OF_MEMORY;
if (!fgets(buf, sizeof(buf), f)) {
rs_error(key, NULL, "invalid key file syntax");
return RS_ERR_KEY_SYNTAX;
}
if (strlen(buf) < 11) {
if (1 > sscanf(buf, "%lX", &key->id)) {
rs_error(key, NULL, "invalid key file syntax");
return RS_ERR_KEY_SYNTAX;
}
if (!fgets(buf, sizeof(buf), f)
|| parse_value(key->n, buf)) {
rs_error(key, NULL, "invalid key file syntax");
return RS_ERR_KEY_SYNTAX;
}
if (!fgets(buf, sizeof(buf), f)
|| parse_value(key->d, buf))
mpz_set_ui(key->d, 0);
else if (verify) {
/* We can't truly verify the key without factoring n (which is
possible, given d, but would take a bit of work.) Instead,
test the key by performing a single RSA encryption and
decryption. */
mpz_init(tmp);
mpz_set_ui(tmp, 17);
mpz_powm(tmp, tmp, tmp, key->n);
mpz_powm(tmp, tmp, key->d, key->n);
if (mpz_cmp_ui(tmp, 17)) {
mpz_clear(tmp);
rs_error(key, NULL, "private key incorrect (de != 1 mod phi(n))");
return RS_ERR_INVALID_KEY;
}
mpz_clear(tmp);
}
mpz_set_ui(key->p, 0);
mpz_set_ui(key->q, 0);
mpz_set_ui(key->qinv, 0);
}
else {
if (parse_value(key->n, buf)) {
rs_error(key, NULL, "invalid key file");
return RS_ERR_KEY_SYNTAX;
}
if (!fgets(buf, sizeof(buf), f)
|| parse_value(key->p, buf)
|| !fgets(buf, sizeof(buf), f)
|| parse_value(key->q, buf)) {
mpz_set_ui(key->p, 0);
mpz_set_ui(key->q, 0);
}
else if (verify) {
/* Verify that p * q = n (of course, that doesn't guarantee that
these are the only factors of n.) */
mpz_init(tmp);
mpz_mul(tmp, key->p, key->q);
if (mpz_cmp(tmp, key->n)) {
mpz_clear(tmp);
rs_error(key, NULL, "private key incorrect (pq != n)");
return RS_ERR_INVALID_KEY;
}
mpz_clear(tmp);
}
mpz_set_ui(key->qinv, 0);
mpz_set_ui(key->d, 0);
key->id = 0;
}
if (mpz_sgn(key->p) && mpz_sgn(key->q)) {
rs_message(2, key, NULL, "Loaded Rabin/RSA private key:");
rs_message(2, key, NULL, " n = %ZX", key->n);
rs_message(2, key, NULL, " p = %ZX", key->p);
rs_message(2, key, NULL, " q = %ZX", key->q);
}
else if (mpz_sgn(key->d)) {
rs_message(2, key, NULL, "Loaded RSA private key:");
rs_message(2, key, NULL, " n = %ZX", key->n);
rs_message(2, key, NULL, " d = %ZX", key->d);
}
else {
rs_message(2, key, NULL, "Loaded public key:");
rs_message(2, key, NULL, " n = %ZX", key->n);
}
return RS_SUCCESS;
}
/*
* Parse a number written in TI's hexadecimal key format.
*/
int rs_parse_key_value(mpz_t dest, /* mpz to store result */
const char* str) /* string to parse */
{
if (parse_value(dest, str)) {
rs_error(NULL, NULL, "invalid key value syntax");
return RS_ERR_KEY_SYNTAX;
}
else {
return RS_SUCCESS;
}
}