/*

rsaglue.c

Author: Tatu Ylonen <ylo@cs.hut.fi>

Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
                   All rights reserved

Created: Tue Apr 11 15:17:50 1995 ylo
Last modified: Wed Jul 12 00:56:39 1995 ylo

Glue code to be able to do RSA encryption/decryption with RSAREF.  The purpose
of this file is to permit compiling this software with RSAREF.  Using RSAREF
may make this software legal for noncommercial use without licencing the
RSA patents.  RSAREF code is only used if the system is configured
using the --with-rsaref configure option.

*/

#include "includes.h"
#include <gmp.h>
#include "ssh.h"
#include "rsa.h"
#include "getput.h"

#ifdef RSAREF

/* This code uses the following RSAREF functions:
     RSAPublicEncrypt
     RSAPrivateDecrypt
     R_RandomInit
     R_RandomUpdate
     R_RandomFinal
   Both functions are exported by RSAREF and are thus part of the available
   interface. */

#define _MD5_H_ /* Kludge to prevent inclusion of rsaref md5.h. */
#include "rsaref2/source/global.h"
#include "rsaref2/source/rsaref.h"

/* Convert an integer from gmp to rsaref representation. */

void gmp_to_rsaref(unsigned char *buf, unsigned int len, MP_INT *value)
{
  unsigned int i;
  MP_INT aux;
  mpz_init_set(&aux, value);
  for (i = len; i >= 4; i -= 4)
    {
      unsigned int limb = mpz_get_ui(&aux);
      PUT_32BIT(buf + i - 4, limb);
      mpz_div_2exp(&aux, &aux, 32);
    }
  for (; i > 0; i--)
    {
      buf[i - 1] = mpz_get_ui(&aux);
      mpz_div_2exp(&aux, &aux, 8);
    }
}

/* Convert an integer from rsaref to gmp representation. */

void rsaref_to_gmp(MP_INT *value, unsigned char *buf, unsigned int len)
{
  unsigned int i;
  mpz_set_ui(value, 0);
  for (i = 0; i + 4 <= len; i += 4)
    {
      unsigned int limb = GET_32BIT(buf + i);
      mpz_mul_2exp(value, value, 32);
      mpz_add_ui(value, value, limb);
    }
  for (; i < len; i++)
    {
      mpz_mul_2exp(value, value, 8);
      mpz_add_ui(value, value, buf[i]);
    }
}

/* Convert a public key from our representation to rsaref representation. */

void rsaref_public_key(R_RSA_PUBLIC_KEY *rsa, RSAPublicKey *key)
{
  rsa->bits = key->bits;
  gmp_to_rsaref(rsa->modulus, MAX_RSA_MODULUS_LEN, &key->n);
  gmp_to_rsaref(rsa->exponent, MAX_RSA_MODULUS_LEN, &key->e);
}

/* Convert a private key from our representation to rsaref representation. 
   Note that some fo the constants are computed a little dfifferently
   in rsaref. */

void rsaref_private_key(R_RSA_PRIVATE_KEY *rsa, RSAPrivateKey *key)
{
  MP_INT aux;
  rsa->bits = key->bits;
  gmp_to_rsaref(rsa->modulus, MAX_RSA_MODULUS_LEN, &key->n);
  gmp_to_rsaref(rsa->publicExponent, MAX_RSA_MODULUS_LEN, &key->e);
  gmp_to_rsaref(rsa->exponent, MAX_RSA_MODULUS_LEN, &key->d);
  gmp_to_rsaref(rsa->prime[0], MAX_RSA_PRIME_LEN, &key->q);
  gmp_to_rsaref(rsa->prime[1], MAX_RSA_PRIME_LEN, &key->p);
  mpz_init_set(&aux, &key->q);
  mpz_sub_ui(&aux, &aux, 1);
  mpz_mod(&aux, &key->d, &aux);
  gmp_to_rsaref(rsa->primeExponent[0], MAX_RSA_PRIME_LEN, &aux);
  mpz_set(&aux, &key->p);
  mpz_sub_ui(&aux, &aux, 1);
  mpz_mod(&aux, &key->d, &aux);
  gmp_to_rsaref(rsa->primeExponent[1], MAX_RSA_PRIME_LEN, &aux);
  gmp_to_rsaref(rsa->coefficient, MAX_RSA_PRIME_LEN, &key->u);
}

/* Performs a public key encrypt operation. */

void rsa_public_encrypt(MP_INT *output, MP_INT *input, RSAPublicKey *key,
			RandomState *random_state)
{
  unsigned char input_data[MAX_RSA_MODULUS_LEN];
  unsigned char output_data[MAX_RSA_MODULUS_LEN];
  unsigned char buf[256];
  unsigned int input_len, output_len, i, input_bits;
  R_RSA_PUBLIC_KEY public_key;
  R_RANDOM_STRUCT rands;

  input_bits = mpz_sizeinbase(input, 2);
  input_len = (input_bits + 7) / 8;
  gmp_to_rsaref(input_data, input_len, input);

  rsaref_public_key(&public_key, key);

  R_RandomInit(&rands);
  for (i = 0; i < 256; i++)
    buf[i] = random_get_byte(random_state);
  R_RandomUpdate(&rands, buf, 256);

  if (RSAPublicEncrypt(output_data, &output_len, input_data, input_len, 
		       &public_key, &rands) != 0)
    fatal("RSAPublicEncrypt failed");

  R_RandomFinal(&rands);

  rsaref_to_gmp(output, output_data, output_len);
}

/* Performs a private key decrypt operation. */

void rsa_private_decrypt(MP_INT *output, MP_INT *input, RSAPrivateKey *key)
{
  unsigned char input_data[MAX_RSA_MODULUS_LEN];
  unsigned char output_data[MAX_RSA_MODULUS_LEN];
  unsigned int input_len, output_len, input_bits;
  R_RSA_PRIVATE_KEY private_key;
  
  input_bits = mpz_sizeinbase(input, 2);
  input_len = (input_bits + 7) / 8;
  gmp_to_rsaref(input_data, input_len, input);

  rsaref_private_key(&private_key, key);

  if (RSAPrivateDecrypt(output_data, &output_len, input_data, input_len,
			&private_key) != 0)
    fatal("RSAPrivateDecrypt failed");

  rsaref_to_gmp(output, output_data, output_len);
}

#else /* RSAREF */

/* Encrypt input using the public key.  Input should be a 256 bit value. */

void rsa_public_encrypt(MP_INT *output, MP_INT *input, RSAPublicKey *key,
			RandomState *state)
{
  MP_INT aux;
  unsigned int i, input_bits, input_len, len;

  input_bits = mpz_sizeinbase(input, 2);
  input_len = (input_bits + 7) / 8;
  len = (key->bits + 7) / 8;

  assert(len >= input_len + 3);
  
  mpz_init_set_ui(&aux, 2);
  for (i = 2; i < len - input_len - 1; i++)
    {
      unsigned int byte;
      do
	byte = random_get_byte(state);
      while (byte == 0);
      mpz_mul_2exp(&aux, &aux, 8);
      mpz_add_ui(&aux, &aux, byte);
    }
  mpz_mul_2exp(&aux, &aux, 8 * (input_len + 1));
  mpz_add(&aux, &aux, input);

  rsa_public(output, &aux, key);
  mpz_clear(&aux);
}

/* Decrypt input using the private key.  Output will become a 256 bit value. */

void rsa_private_decrypt(MP_INT *output, MP_INT *input, RSAPrivateKey *key)
{
  MP_INT aux;
  unsigned int len, i;
  unsigned char value[1024];

  rsa_private(output, input, key);

  len = (key->bits + 7) / 8;

  mpz_init_set(&aux, output);
  for (i = len; i >= 4; i -= 4)
    {
      unsigned int limb = mpz_get_ui(&aux);
      PUT_32BIT(value + i - 4, limb);
      mpz_div_2exp(&aux, &aux, 32);
    }
  for (; i > 0; i--)
    {
      value[i - 1] = mpz_get_ui(&aux);
      mpz_div_2exp(&aux, &aux, 8);
    }
  mpz_clear(&aux);

  if (value[0] != 0 || value[1] != 2)
    fatal("Bad result from rsa_private_decrypt");

  for (i = 2; i < len && value[i]; i++)
    ;

  mpz_mod_2exp(output, output, 8 * (len - i - 1));
}

#endif /* RSAREF */
