/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library 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
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA */

/* password checking routines */
/*****************************************************************************
  The main idea is that no password are sent between client & server on
  connection and that no password are saved in mysql in a decodable form.

  On connection a random string is generated and sent to the client.
  The client generates a new string with a random generator inited with
  the hash values from the password and the sent string.
  This 'check' string is sent to the server where it is compared with
  a string generated from the stored hash_value of the password and the
  random string.

  The password is saved (in user.password) by using the PASSWORD() function in
  mysql.

  Example:
    update user set password=PASSWORD("hello") where user="test"
  This saves a hashed number as a string in the password field.
*****************************************************************************/

#include <global.h>
#include <my_sys.h>
#include <m_string.h>
#include "mysql.h"


void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
{						/* For mysql 3.21.# */
#ifdef HAVE_purify
  bzero((char*) rand_st,sizeof(*rand_st));		/* Avoid UMC varnings */
#endif
  rand_st->max_value= 0x3FFFFFFFL;
  rand_st->max_value_dbl=(double) rand_st->max_value;
  rand_st->seed1=seed1%rand_st->max_value ;
  rand_st->seed2=seed2%rand_st->max_value;
}

static void old_randominit(struct rand_struct *rand_st,ulong seed1)
{						/* For mysql 3.20.# */
  rand_st->max_value= 0x01FFFFFFL;
  rand_st->max_value_dbl=(double) rand_st->max_value;
  seed1%=rand_st->max_value;
  rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
}

double rnd(struct rand_struct *rand_st)
{
  rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
  rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
  return (((double) rand_st->seed1)/rand_st->max_value_dbl);
}

void hash_password(ulong *result, const char *password)
{
  register ulong nr=1345345333L, add=7, nr2=0x12345671L;
  ulong tmp;
  for (; *password ; password++)
  {
    if (*password == ' ' || *password == '\t')
      continue;			/* skipp space in password */
    tmp= (ulong) (uchar) *password;
    nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
    nr2+=(nr2 << 8) ^ nr;
    add+=tmp;
  }
  result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
  result[1]=nr2 & (((ulong) 1L << 31) -1L);
  return;
}

void make_scrambled_password(char *to,const char *password)
{
  ulong hash_res[2];
  hash_password(hash_res,password);
  sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}

#ifdef RISCOS
static  unsigned int char_val(char X)
{
  return (uint) (X >= '0' && X <= '9' ? X-'0' :
		 X >= 'A' && X <= 'Z' ? X-'A'+10 :
		 X-'a'+10);
}
#else
static inline unsigned int char_val(char X)
{
  return (uint) (X >= '0' && X <= '9' ? X-'0' :
		 X >= 'A' && X <= 'Z' ? X-'A'+10 :
		 X-'a'+10);
}
#endif

/*
** This code assumes that len(password) is divideable with 8 and that
** res is big enough (2 in mysql)
*/

void get_salt_from_password(ulong *res,const char *password)
{
  res[0]=res[1]=0;
  if (password)
  {
    while (*password)
    {
      ulong val=0;
      uint i;
      for (i=0 ; i < 8 ; i++)
	val=(val << 4)+char_val(*password++);
      *res++=val;
    }
  }
  return;
}

void make_password_from_salt(char *to, ulong *hash_res)
{
  sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}


/*
 * Genererate a new message based on message and password
 * The same thing is done in client and server and the results are checked.
 */

char *scramble(char *to,const char *message,const char *password,
	       my_bool old_ver)
{
  struct rand_struct rand_st;
  ulong hash_pass[2],hash_message[2];
  if (password && password[0])
  {
    char *to_start=to;
    hash_password(hash_pass,password);
    hash_password(hash_message,message);
    if (old_ver)
      old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
    else
      randominit(&rand_st,hash_pass[0] ^ hash_message[0],
		 hash_pass[1] ^ hash_message[1]);
    while (*message++)
      *to++= (char) (floor(rnd(&rand_st)*31)+64);
    if (!old_ver)
    {						/* Make it harder to break */
      char extra=(char) (floor(rnd(&rand_st)*31));
      while (to_start != to)
	*(to_start++)^=extra;
    }
  }
  *to=0;
  return to;
}


my_bool check_scramble(const char *scrambled, const char *message,
		       ulong *hash_pass, my_bool old_ver)
{
  struct rand_struct rand_st;
  ulong hash_message[2];
  char buff[16],*to,extra;			/* Big enough for check */
  const char *pos;

  hash_password(hash_message,message);
  if (old_ver)
    old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
  else
    randominit(&rand_st,hash_pass[0] ^ hash_message[0],
	       hash_pass[1] ^ hash_message[1]);
  to=buff;
  for (pos=scrambled ; *pos ; pos++)
    *to++=(char) (floor(rnd(&rand_st)*31)+64);
  if (old_ver)
    extra=0;
  else
    extra=(char) (floor(rnd(&rand_st)*31));
  to=buff;
  while (*scrambled)
  {
    if (*scrambled++ != (char) (*to++ ^ extra))
      return 1;					/* Wrong password */
  }
  return 0;
}


/*
  MySQL 4.1 password scramble (mysql_native_password), so this 3.23 client
  can log in to MySQL 4.1 and later servers:

    stage1= SHA1(password), stage2= SHA1(stage1)
    to= stage1 XOR SHA1(message . stage2)

  message is the server's SCRAMBLE_LENGTH byte seed; to gets
  SCRAMBLE_LENGTH bytes (not NUL terminated).
*/

typedef struct
{
  uint32 h[5];
  uint32 length;				/* Bytes hashed so far */
  uchar  block[64];
  uint   used;					/* Bytes in block */
} sha1_ctx;

#define SHA1_ROL(x,n) (((x) << (n)) | ((x) >> (32 - (n))))

static void sha1_transform(sha1_ctx *ctx)
{
  uint32 w[80], a, b, c, d, e, t;
  uint i;

  for (i= 0; i < 16; i++)
    w[i]= ((uint32) ctx->block[i*4] << 24) | ((uint32) ctx->block[i*4+1] << 16) |
          ((uint32) ctx->block[i*4+2] << 8) | (uint32) ctx->block[i*4+3];
  for (; i < 80; i++)
    w[i]= SHA1_ROL(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
  a= ctx->h[0]; b= ctx->h[1]; c= ctx->h[2]; d= ctx->h[3]; e= ctx->h[4];
  for (i= 0; i < 80; i++)
  {
    if (i < 20)
      t= ((b & c) | (~b & d)) + 0x5A827999UL;
    else if (i < 40)
      t= (b ^ c ^ d) + 0x6ED9EBA1UL;
    else if (i < 60)
      t= ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDCUL;
    else
      t= (b ^ c ^ d) + 0xCA62C1D6UL;
    t+= SHA1_ROL(a, 5) + e + w[i];
    e= d; d= c; c= SHA1_ROL(b, 30); b= a; a= t;
  }
  ctx->h[0]+= a; ctx->h[1]+= b; ctx->h[2]+= c; ctx->h[3]+= d; ctx->h[4]+= e;
  ctx->used= 0;
}

static void sha1_init(sha1_ctx *ctx)
{
  ctx->h[0]= 0x67452301UL;
  ctx->h[1]= 0xEFCDAB89UL;
  ctx->h[2]= 0x98BADCFEUL;
  ctx->h[3]= 0x10325476UL;
  ctx->h[4]= 0xC3D2E1F0UL;
  ctx->length= 0;
  ctx->used= 0;
}

static void sha1_update(sha1_ctx *ctx, const uchar *data, uint len)
{
  ctx->length+= len;
  while (len--)
  {
    ctx->block[ctx->used++]= *data++;
    if (ctx->used == 64)
      sha1_transform(ctx);
  }
}

static void sha1_final(sha1_ctx *ctx, uchar *digest)
{
  uint32 bits= ctx->length << 3;
  uint i;

  ctx->block[ctx->used++]= 0x80;
  if (ctx->used > 56)
  {
    while (ctx->used < 64)
      ctx->block[ctx->used++]= 0;
    sha1_transform(ctx);
  }
  while (ctx->used < 56)
    ctx->block[ctx->used++]= 0;
  ctx->block[56]= (uchar) (ctx->length >> 29);	/* High bits of bit count */
  ctx->block[57]= ctx->block[58]= ctx->block[59]= 0;
  ctx->block[60]= (uchar) (bits >> 24);
  ctx->block[61]= (uchar) (bits >> 16);
  ctx->block[62]= (uchar) (bits >> 8);
  ctx->block[63]= (uchar) bits;
  sha1_transform(ctx);
  for (i= 0; i < 20; i++)
    digest[i]= (uchar) (ctx->h[i >> 2] >> (24 - 8 * (i & 3)));
}

void scramble_41(char *to, const char *message, const char *password)
{
  sha1_ctx ctx;
  uchar stage1[20], stage2[20], hash[20];
  uint i;

  sha1_init(&ctx);
  sha1_update(&ctx, (const uchar*) password, (uint) strlen(password));
  sha1_final(&ctx, stage1);
  sha1_init(&ctx);
  sha1_update(&ctx, stage1, 20);
  sha1_final(&ctx, stage2);
  sha1_init(&ctx);
  sha1_update(&ctx, (const uchar*) message, SCRAMBLE_LENGTH);
  sha1_update(&ctx, stage2, 20);
  sha1_final(&ctx, hash);
  for (i= 0; i < 20; i++)
    to[i]= (char) (hash[i] ^ stage1[i]);
}
