/*
* 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"
/*
* Compute square root of x modulo p, where p === 3 (mod 4).
*
* (Assume that (x|p) = 1.)
*
* Notice that:
*
* p = 4k + 3
*
* x^[(p-1)/2] = x^(2k+1) = (x|p) = 1
*
* x^(2k+2) = x
*
* [x^(k+1)]^2 = x
*
* so x^(k+1) = x^[(p+1)/4] is a square root of x.
*/
static void mpz_sqrtm_3 (mpz_t res, /* mpz to store result */
const mpz_t x, /* number to get square root of */
const mpz_t p) /* prime modulus === 3 (mod 4) */
{
mpz_add_ui(res, p, 1);
mpz_fdiv_q_2exp(res, res, 2); /* (p + 1)/4 */
mpz_powm(res, x, res, p);
}
/*
* Compute square root of x modulo p, where p === 5 (mod 8).
*
* (Assume that (x|p) = 1.)
*
* Notice that:
*
* p = 4k + 1
*
* x^[(p-1)/2] = x^(2k) = (x|p) = 1
*
* x^[(k+1)/2]^2 * x^(4k-1) = x^(5k) = x^k
*
* Since x^k^2 = 1, x^k = +/- 1.
*
* CASE 1:
* If x^k = 1, x^[(k+1)/2]^2 = x, so x^[(k+1)/2] = x^[(p+3)/8] is
* the square root of x.
*
* CASE 2:
* Otherwise, x^[(k+1)/2]^2 = -x; we need to find a square root of
* -1.
*
* Since (2|p) = -1, 2^[(p-1)/2] = 2^(2k) = -1, so (2^k)^2 = -1
*
* (x^[(k+1)/2] * 2^k)^2 = -x * -1 = x
*
* so x^[(k+1)/2] * 2^k = x^[(p+3)/8] * 2^[(p-1)/4] is the square
* root of x.
*/
static void mpz_sqrtm_5 (mpz_t res, /* mpz to store result */
const mpz_t x, /* number to get square root of */
const mpz_t p) /* prime modulus === 5 (mod 8) */
{
mpz_t a, b;
mpz_init(a);
mpz_init(b);
mpz_add_ui(a, p, 3);
mpz_fdiv_q_2exp(b, a, 3);
mpz_powm(res, x, b, p); /* x ^ (p+3)/8 */
/* Check if res^2 = x */
mpz_mul(a, res, res);
mpz_sub(b, a, x);
mpz_mod(a, b, p);
if (0 != mpz_sgn(a)) {
mpz_sub_ui(a, p, 1);
mpz_fdiv_q_2exp(b, a, 2);
mpz_set_ui(a, 2);
mpz_powm(a, a, b, p); /* 2 ^ (p-1)/4 */
mpz_mul(res, res, a);
}
mpz_clear(a);
mpz_clear(b);
}
/*
* Compute square root of x modulo p.
*
* This still won't work with p === 1 mod 8, but then, TI's system
* won't work at all for 50% of apps if one of your factors is 1 mod
* 8. (See the discussion of f values below.)
*
*/
static void mpz_sqrtm (mpz_t res, /* mpz to store result */
const mpz_t x, /* number to get square root of */
const mpz_t p) /* prime modulus === 3, 5, or 7
(mod 8) */
{
if ((mpz_get_ui(p) % 8) == 5)
mpz_sqrtm_5(res, x, p);
else
mpz_sqrtm_3(res, x, p);
}
/*
* Compute x s.t. x === r (mod p) and x === s (mod q).
*
* We compute this as:
*
* [(r-s) * q^-1 mod p] * q + s
*
*/
static void mpz_crt(mpz_t res, /* mpz to store result */
const mpz_t r, /* root modulo p */
const mpz_t s, /* root modulo q */
const mpz_t p, /* first modulus */
const mpz_t q, /* second modulus */
const mpz_t qinv) /* q^(p-2) mod p */
{
/* ((r - s) */
mpz_sub(res, r, s);
/* * q^-1) */
mpz_mul(res, res, qinv);
mpz_mod(res, res, p);
/* * q + s */
mpz_mul(res, res, q);
mpz_add(res, res, s);
}
/*
* Compute the T_f transform modulo n.
*
* Because only one quarter of the possible hashes can be signed with
* a given key, we need to transform the hash. First, we want to
* ensure that the result is nonzero, so we shift the hash by 8 bits
* and add a 1 to the end. The resulting number is called m'.
*
* Second, we want to multiply it by a number k whose Legendre symbols
* (k|p) and (k|q) are known, so that (km'|p) = (k|p)(m'|p) = 1 and
* (km'|q) = (k|q)(km'|q) = 1. Since we need both to be true
* simultaneously, regardless of the values of (m'|p) and (m'|q), we
* clearly need four possible values of k.
*
* As it happens, TI's keys all follow a precise format: they all have
* p === 3 and q === 7 (mod 8). As a result, we know that
*
* (-1|p) = (-1|q) = -1
*
* (2|p) = -1, (2|q) = 1
*
* So TI has defined the following transformation functions:
*
* T_0(x) = -2x'
* T_1(x) = -x'
* T_2(x) = x'
* T_3(x) = 2x'
*
* where x' = 256x + 1.
*
* In the usual case of p === 3 and q === 7 (mod 8), then, two of the
* possible (T_f(m)|p) will equal 1:
*
* If (m'|p) = 1, then (T_0(m)|p) = (T_2(m)|p) = 1.
* If (m'|p) = -1, then (T_1(m)|p) = (T_3(m)|p) = 1.
*
* Two of the possible (T_f(m)|q) will equal 1:
*
* If (m'|q) = 1, then (T_2(m)|q) = (T_3(m)|q) = 1.
* If (m'|q) = -1, then (T_0(m)|q) = (T_1(m)|q) = 1.
*
* Thus we can choose exactly one f value with
* (T_f(m)|p) = (T_f(m)|q) = 1.
*
* If r === 5 (mod 8) is a prime, (-1|r) = 1, while (2|r) = -1. Thus
* a similar logic holds:
*
* If (m'|r) = 1, then (T_1(m)|r) = (T_2(m)|r) = 1.
* If (m'|r) = -1, then (T_0(m)|r) = (T_3(m)|r) = 1.
*
* So if {p,q} === {3,5}, {5,7}, or {3,7} (mod 8), given any m, we can
* pick an f with (T_f(m)|p) = (T_f(m)|q) = 1.
*
*/
static void applyf(mpz_t res, /* mpz to store result */
const mpz_t m, /* MD5 hash */
const mpz_t n, /* public key */
int f) /* f (0, 1, 2, 3) */
{
mpz_mul_ui(res, m, 256);
mpz_add_ui(res, res, 1);
switch (f) {
case 0:
mpz_add(res, res, res);
case 1:
mpz_sub(res, n, res);
break;
case 2:
break;
case 3:
mpz_add(res, res, res);
break;
}
}
/*
* Compute the Rabin signature with a given f.
*/
static void rabsigf(mpz_t res, /* mpz to store result */
const mpz_t m, /* MD5 hash */
const mpz_t n, /* public key */
const mpz_t p, /* first factor */
const mpz_t q, /* second factor */
const mpz_t qinv, /* q^(p-2) mod p */
int f, /* f (0, 1, 2, 3) */
int rootnum) /* root number (0, 1, 2, 3) */
{
mpz_t mm;
mpz_t r,s;
mpz_init(r);
mpz_init(s);
mpz_init(mm);
applyf(mm, m, n, f);
mpz_sqrtm(r, mm, p);
mpz_sqrtm(s, mm, q);
if (rootnum & 1) {
mpz_sub(r, p, r);
}
if (rootnum & 2) {
mpz_sub(s, q, s);
}
mpz_crt(res, r, s, p, q, qinv);
mpz_clear(r);
mpz_clear(s);
mpz_clear(mm);
}
/*
* Table of f values.
*
* Remember that
*
* f = 0 corresponds to multiplying by -2
* f = 1 corresponds to multiplying by -1
* f = 2 corresponds to multiplying by 1
* f = 3 corresponds to multiplying by 2
*/
static const int ftab[36] = {
/************* (m'|p) = (m'|q) = 1 */
/********** (m'|p) = -1, (m'|q) = 1 */
/****** (m'|p) = 1, (m'|q) = -1 */
/*** (m'|p) = (m'|q) = -1 */
/* p === 3, q === 3 */
2, 99, 99,1, /* (-1|p) = (-1|q) = -1 ==> if both -1, multiply by -1 */
/* p === 3, q === 5 */
2, 1, 0, 3, /* (-1|p) = -1, (-1|q) = 1 ==> if (m'|p) = -1, multiply by -1 */
/* (-2|p) = 1, (-2|q) = -1 ==> if (m'|q) = -1, multiply by -2 */
/* p === 3, q === 7 */
2, 3, 0, 1, /* (2|p) = -1, (2|q) = 1 ==> if (m'|p) = -1, multiply by 2 */
/* (-2|p) = 1, (-2|q) = -1 ==> if (m'|q) = -1, multiply by -2 */
/* p === 5, q === 3 */
2, 0, 1, 3,
/* p === 5, q === 5 */
2, 99, 99,3, /* (2|p) = (2|q) = -1 ==> if both -1, multiply by 2 */
/* p === 5, q === 7 */
2, 3, 1, 0, /* (2|p) = -1, (2|q) = 1 ==> if (m'|p) = -1, multiply by 2 */
/* (-1|p) = 1, (-1|q) = -1 ==> if (m'|q) = -1, multiply by -1 */
/* p === 7, q === 3 */
2, 0, 3, 1,
/* p === 7, q === 5 */
2, 1, 3, 0,
/* p === 7, q === 7 */
2, 99, 99,1 /* (-1|p) = (-1|q) = -1 ==> if both -1, multiply by -1 */
};
/*
* Compute the Rabin signature and the useful value of f.
*/
int rs_sign_rabin(mpz_t res, /* mpz to store signature */
int* f, /* f value chosen */
const mpz_t hash, /* MD5 hash of app */
int rootnum, /* root number (0, 1, 2, 3) */
RSKey* key) /* key structure */
{
mpz_t mm;
int mLp, mLq;
int pm8, qm8;
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->p) || !mpz_sgn(key->q)) {
rs_error(key, NULL, "unable to sign: private key missing");
return RS_ERR_MISSING_PRIVATE_KEY;
}
mpz_init(mm);
/* Calculate q^-1 if necessary */
if (!mpz_sgn(key->qinv)) {
#ifndef USE_MPZ_GCDEXT
mpz_sub_ui(mm, key->p, 2);
mpz_powm(key->qinv, key->q, mm, key->p);
#else
mpz_gcdext(mm, key->qinv, NULL, key->q, key->p);
if (mpz_cmp_ui(mm, 1)) {
mpz_clear(mm);
rs_error(key, NULL, "unable to sign: unsuitable key");
return RS_ERR_UNSUITABLE_RABIN_KEY;
}
#endif
}
applyf(mm, hash, key->n, 2);
mLp = mpz_legendre(mm, key->p);
mLq = mpz_legendre(mm, key->q);
pm8 = mpz_get_ui(key->p) % 8;
qm8 = mpz_get_ui(key->q) % 8;
if (pm8 == 1 || qm8 == 1 || (pm8 % 2) == 0 || (qm8 % 2) == 0) {
mpz_clear(mm);
rs_error(key, NULL, "unable to sign: unsuitable key");
return RS_ERR_UNSUITABLE_RABIN_KEY;
}
*f = ftab[(mLp == 1 ? 0 : 1) +
(mLq == 1 ? 0 : 2) +
(((qm8 - 3) / 2) * 4) +
(((pm8 - 3) / 2) * 12)];
if (*f == 99) {
mpz_clear(mm);
rs_error(key, NULL, "unable to sign: unsuitable key");
return RS_ERR_UNSUITABLE_RABIN_KEY;
}
rabsigf(res, hash, key->n, key->p, key->q, key->qinv, *f, rootnum);
mpz_clear(mm);
return RS_SUCCESS;
}
/* Check that the given Rabin signature is valid. */
int rs_validate_rabin (const mpz_t sig, /* purported signature of app */
int f, /* f value */
const mpz_t hash, /* MD5 hash of app */
const RSKey* key) /* key structure */
{
mpz_t a, b;
int result;
if (!mpz_sgn(key->n)) {
rs_error(key, NULL, "unable to validate: public key missing");
return RS_ERR_MISSING_PUBLIC_KEY;
}
if (f < 0 || f > 3)
return RS_SIGNATURE_INCORRECT;
mpz_init(a);
mpz_init(b);
mpz_mul(a, sig, sig);
mpz_mod(a, a, key->n);
applyf(b, hash, key->n, f);
result = mpz_cmp(a, b);
mpz_clear(a);
mpz_clear(b);
return (result ? RS_SIGNATURE_INCORRECT : RS_SUCCESS);
}