/* $Id: rabin.C,v 1.11 2001/08/19 00:27:16 dm Exp $ */ /* * * Copyright (C) 1998 David Mazieres (dm@uun.org) * * 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 2, 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ /* Rabin-Williams public key algorithm. From: * A Modification of the RSA Public-Key Encryption Procedure. * H. C. Williams, IEEE Transactions on Information Theory, * Vol. IT-26, No. 6, November, 1980. * * The cipher is based on Rabin's original signature scheme. This is * NOT RSA, and is NOT covered by the RSA patent. (That's not to say * they couldn't try to sue you anyway for using this, but it's * unlikely. However, if you use this code, you must agree to assume * all responsibility for patent infringement.) * * Two prime numbers p, q, chosen such that: * p = 3 mod 8 * q = 7 mod 8 * * [Note: This means pq = 5 mod 8, so J(2, pq) = -1.] * * Define N and k as: * N = pq * k = 1/2 (1/4 * (p-1) * (q-1) + 1); * [k = ((p-1) * (q-1) + 4) / 8;] * * The public key is (N). * The private key is (N, k). * * Define the following four operations. (Note D2 can only be * performed with the secret key.) * * { 4(2M+1) if J(2M+1, N) = 1 * E1(M) = { 2(2M+1) if J(2M+1, N) = -1 * { key cracked! if J(2M+1, N) = 0 (completely improbable) * * E2(M) = M^2 % N * * D2(M) = M^k % N * * { (M/4-1)/2 when M = 0 mod 4 * D1(M) = { ((N-M)/4-1)/2 when M = 1 mod 4 * { (M/2-1)/2 when M = 2 mod 4 * { ((N-M)/2-1)/2 when M = 3 mod 4 * * Public key operations on messages M < (N-4)/8: * Encrypt: E2(E1(M)) * Decrypt: D1(D2(M)) * Sign: D2(E1(M)) * Verify: D1(E2(M)) */ #include "crypt.h" #include "rabin.h" #include "prime.h" INITFN (scrubinit); static void scrubinit () { mp_setscrub (); } bool rabin_pub::E1 (bigint &m, const bigint &in) const { m = in << 1; m += 1; switch (jacobi (m, n)) { case 1: m <<= 2; break; case -1: m <<= 1; break; case 0: warn << "Key factored! jacobi (" << m << ", " << n << ") = 0\n"; return false; } if (m >= n) { warn ("rabin_pub::E1: input too large\n"); return false; } return true; } void rabin_pub::D1 (bigint &m, const bigint &in) const { switch (in.getui () & 3) { case 0: m = in - 4; m >>= 3; break; case 1: m = n - in; m -= 4; m >>= 3; break; case 2: m = in - 2; m >>= 2; break; case 3: m = n - in; m -= 2; m >>= 2; break; } } void rabin_pub::E2 (bigint &m, const bigint &in) const { //m = in * in; mpz_square (&m, &in); m %= n; } /* Calculate m = {in}^k % n. Use Chinese remainder theorem for speed. */ void rabin_priv::D2 (bigint &m, const bigint &in, int rsel) const { /* Multiply input by random r = (ri)^{-2} mod n, to randomize the * timing of the modular reductions. */ bigint r, ri; r = random_bigint (n.nbits () - 1); mpz_square (&ri, &r); ri %= n; mpz_square (&r, &ri); r = invert (r, n); r *= in; r %= n; /* find op, oq such that out % p = op, out % q = oq */ bigint op (powm (r, kp, p)); bigint oq (powm (r, kq, q)); /* rsel selects which of 4 square roots */ if (rsel & 1) op = p - op; if (rsel & 2) oq = q - oq; /* m = (((op - oq) * u) % p) * q + oq; */ m = op - oq; m *= u; m = mod (m, p); m *= q; m += oq; /* Divide r back out */ m *= ri; m %= n; } void rabin_priv::init () { assert (p < q); u = mod (invert (q, p), p); bigint p1 = p - 1; bigint q1 = q - 1; kp = (p1 * q1 + 4) >> 3; kq = kp % q1; kp %= p1; } rabin_priv::rabin_priv (const bigint &n1, const bigint &n2) : rabin_pub (n1 * n2), p (n1), q (n2) { init (); } ptr rabin_priv::make (const bigint &n1, const bigint &n2) { if (n1 == n2 || n1 <= 1 || n2 <= 1 || !n1.probab_prime (5) || !n2.probab_prime (5)) return NULL; return n1 < n2 ? New refcounted (n1, n2) : New refcounted (n2, n1); } static const u_int sieve_3_mod_4[4] = { 3, 2, 1, 4 }; static const u_int sieve_3_mod_8[8] = { 3, 2, 1, 8, 7, 6, 5, 4 }; static const u_int sieve_7_mod_8[8] = { 7, 6, 5, 4, 3, 2, 1, 8 }; rabin_priv rabin_keygen (size_t bits, u_int iter) { random_init (); bigint p1 = random_prime (bits/2 + (bits & 1), sieve_3_mod_4, 4, iter); bigint p2 = random_prime (bits/2 + 1, p1.getbit (2) ? sieve_3_mod_8 : sieve_7_mod_8, 8, iter); if (p1 > p2) swap (p1, p2); return rabin_priv (p1, p2); }