138 lines
3.5 KiB
C
138 lines
3.5 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "rabbitsign.h"
|
|
#include "internal.h"
|
|
|
|
#define VALIDATION_EXPONENT 17
|
|
|
|
/*
|
|
* Calculate the RSA signing exponent.
|
|
*
|
|
* The validation exponent, e, is 17 for all TI-related RSA
|
|
* signatures. The signing exponent, d, depends on n, and is
|
|
* calculated so that e * d === 1 (mod (p-1)(q-1)).
|
|
*
|
|
* (This means that for any number x,
|
|
*
|
|
* x^(e * d) = x * x^[k0 * (p-1)] === x * 1 (mod p),
|
|
*
|
|
* and likewise
|
|
*
|
|
* x^(e * d) = x * x^[k1 * (q-1)] === x * 1 (mod q).
|
|
*
|
|
* Therefore (Chinese remainder theorem) x^(e * d) === x (mod n).
|
|
*
|
|
* Note that there is no way of calculating d without knowing the
|
|
* factors of n; this is a key point in the security of RSA.)
|
|
*/
|
|
static int get_exponent(mpz_t res, /* mpz to store result */
|
|
int e, /* validation exponent */
|
|
const mpz_t p, /* first factor */
|
|
const mpz_t q) /* second fatctor */
|
|
{
|
|
mpz_t a, b;
|
|
mpz_init(a);
|
|
mpz_init(b);
|
|
|
|
mpz_sub_ui(a, p, 1);
|
|
mpz_sub_ui(b, q, 1);
|
|
mpz_mul(a, a, b);
|
|
|
|
mpz_set_ui(b, e);
|
|
|
|
mpz_gcdext(b, res, NULL, b, a);
|
|
if (mpz_cmp_ui(b, 1)) {
|
|
mpz_clear(a);
|
|
mpz_clear(b);
|
|
return RS_ERR_UNSUITABLE_RSA_KEY;
|
|
}
|
|
|
|
mpz_mod(res, res, a);
|
|
|
|
mpz_clear(a);
|
|
mpz_clear(b);
|
|
return RS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Compute an RSA signature.
|
|
*
|
|
* This is simply the hash raised to the d-th power mod n (where d is
|
|
* defined above.)
|
|
*/
|
|
int rs_sign_rsa(mpz_t res, /* mpz to store signature */
|
|
const mpz_t hash, /* MD5 hash of app */
|
|
RSKey* key) /* key structure */
|
|
{
|
|
if (!mpz_sgn(key->n)) {
|
|
rs_error(key, NULL, "unable to sign: public key missing");
|
|
return RS_ERR_MISSING_PUBLIC_KEY;
|
|
}
|
|
|
|
if (!mpz_sgn(key->d)) {
|
|
if (!mpz_sgn(key->p) || !mpz_sgn(key->q)) {
|
|
rs_error(key, NULL, "unable to sign: private key missing");
|
|
return RS_ERR_MISSING_PRIVATE_KEY;
|
|
}
|
|
if (get_exponent(key->d, VALIDATION_EXPONENT, key->p, key->q)) {
|
|
rs_error(key, NULL, "unable to sign: unsuitable key");
|
|
return RS_ERR_UNSUITABLE_RSA_KEY;
|
|
}
|
|
}
|
|
|
|
mpz_powm(res, hash, key->d, key->n);
|
|
return RS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Check that the given RSA signature is valid.
|
|
*
|
|
* To do this, we raise the signature to the 17th power mod n, and see
|
|
* if it matches the hash.
|
|
*/
|
|
int rs_validate_rsa(const mpz_t sig, /* purported signature of app */
|
|
const mpz_t hash, /* MD5 hash of app */
|
|
const RSKey* key) /* key structure */
|
|
{
|
|
mpz_t e, m;
|
|
int result;
|
|
|
|
if (!mpz_sgn(key->n)) {
|
|
rs_error(key, NULL, "unable to validate: public key missing");
|
|
return RS_ERR_MISSING_PUBLIC_KEY;
|
|
}
|
|
|
|
mpz_init(e);
|
|
mpz_init(m);
|
|
|
|
mpz_set_ui(e, VALIDATION_EXPONENT);
|
|
mpz_powm(m, sig, e, key->n);
|
|
result = mpz_cmp(hash, m);
|
|
|
|
mpz_clear(e);
|
|
mpz_clear(m);
|
|
return (result ? RS_SIGNATURE_INCORRECT : RS_SUCCESS);
|
|
}
|