/*
 * Cylink Corporation © 1998
 * 
 * This software is licensed by Cylink to the Internet Software Consortium to
 * promote implementation of royalty free public key cryptography within IETF
 * standards.  Cylink wishes to expressly thank the contributions of Dr.
 * Martin Hellman, Whitfield Diffie, Ralph Merkle and Stanford University for
 * their contributions to Internet Security.  In accordance with the terms of
 * this license, ISC is authorized to distribute and sublicense this software
 * for the practice of IETF standards.  
 *
 * The software includes BigNum, written by Colin Plumb and licensed by Philip
 * R. Zimmermann for royalty free use and distribution with Cylink's
 * software.  Use of BigNum as a stand alone product or component is
 * specifically prohibited.
 *
 * Disclaimer of All Warranties. THIS SOFTWARE IS BEING PROVIDED "AS IS",
 * WITHOUT ANY EXPRESSED OR IMPLIED WARRANTY OF ANY KIND WHATSOEVER. IN
 * PARTICULAR, WITHOUT LIMITATION ON THE GENERALITY OF THE FOREGOING, CYLINK
 * MAKES NO REPRESENTATION OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
 * PURPOSE.
 *
 * Cylink or its representatives shall not be liable for tort, indirect,
 * special or consequential damages such as loss of profits or loss of
 * goodwill from the use or inability to use the software for any purpose or
 * for any reason whatsoever.
 *
 * EXPORT LAW: Export of the Foundations Suite may be subject to compliance
 * with the rules and regulations promulgated from time to time by the Bureau
 * of Export Administration, United States Department of Commerce, which
 * restrict the export and re-export of certain products and technical data.
 * If the export of the Foundations Suite is controlled under such rules and
 * regulations, then the Foundations Suite shall not be exported or
 * re-exported, directly or indirectly, (a) without all export or re-export
 * licenses and governmental approvals required by any applicable laws, or (b)
 * in violation of any applicable prohibition against the export or re-export
 * of any part of the Foundations Suite. All export licenses for software
 * containing the Foundations Suite are the sole responsibility of the licensee.
 */
 
/****************************************************************************
*  FILENAME:  dss.c   PRODUCT NAME: CRYPTOGRAPHIC TOOLKIT
*
*  FILE STATUS:
*
*  DESCRIPTION:     Cryptographic Toolkit Functions File
*                   Digital Signature Algorithms
*  PUBLIC FUNCTIONS:
*                                   uchar  *RVAL )
*      int GenDSSKey( u_int16_t dss_p_bytes, uchar  *dss_p,
*                     uchar *dss_q,       uchar  *dss_g,
*                     uchar  *dss_x,      uchar  *dss_y,
*                                         uchar  *XKEY )
*
*      int GenDSSNumber( uchar *dss_k,   uchar dss_q,
*                                        uchar *KKEY )
*       int GenDSSParameters( u_int16_t dss_p_bytes, uchar  *dss_p,
*                              uchar  *dss_q,      uchar  *dss_g,
*                                                  uchar  *RVAL );
*
*   Copyright (c) Cylink Corporation 1996. All rights reserved.
*
*  REVISION  HISTORY:
*
*
****************************************************************************/

/****************************************************************************
*  INCLUDE FILES
****************************************************************************/

#include "port_before.h"
#include <sys/types.h>
#include "cylink.h"
#include "ctk_endian.h"
#include "c_asm.h"

/* system files */
#ifdef VXD
#include <vtoolsc.h>
#else
#include <stdlib.h>
#include <string.h>
#endif

/* program files */
#ifdef VXD
#include "tkvxd.h"
#endif
#include "toolkit.h"
#include "cencrint.h"
#define  INITIALIZ_PQG   /*TKL01101*/
#include "dssnum.h"      /*TKL01101*/
#include "sha.h"
#include "port_after.h"

#define  BEGIN_PROCESSING do {
#define  END_PROCESSING  } while (0);
#define  ERROR_BREAK break
#define  CONTINUE continue

#define  BEGIN_LOOP do {
#define  END_LOOP  } while (1);
#define  BREAK break


extern u_int16_t DataOrder;

/****************************************************************************
*  NAME:  int GenDSSSignature( u_int16_t dss_p_bytes,
*                              uchar  *dss_p,
*                              uchar  *dss_q,
*                              uchar  *dss_g,
*                              uchar  *dss_x,
*                              uchar  *dss_k,
*                              uchar  *r,
*                              uchar  *s,
*                              uchar  *hash_result)
*
*  DESCRIPTION:  Compute a DSS Signature
*
*  INPUTS:
*      PARAMETERS:
*          u_int16_t dss_p_bytes  Length of dss_p
*          uchar *dss_p        Pointer to p prime
*          uchar *dss_q        Pointer to q prime
*          uchar *dss_g        Pointer to g
*          uchar *dss_x        Pointer to secret number
*          uchar *dss_k        Pointer to random secret number
*          uchar *hash_result  Pointer to message hashing result
*
*
*  OUTPUT:
*      PARAMETERS:
*          uchar *r           Pointer to r part of signature
*          uchar *s           Pointer to s part of signature
*
*      RETURN:
*          E_SUCCESS         No errors
*          E_DSS_LEN         Invalid length for dss_p
*          ERR_ALLOC         Insufficient memory
*
*  REVISION HISTORY:
*
*  24 Sep 94   KPZ     Initial release
*  14 Oct 94   GKL     Second version (big endian support)
*  26 Oct 94   GKL     (alignment for big endian support & ERR_ALLOC)
*  08 Dec 94   GKL     Changed Expo call
*
****************************************************************************/

int GenDSSSignature( u_int16_t dss_p_bytes,
                     uchar  *dss_p,
                     uchar  *dss_q,
                     uchar  *dss_g,
                     uchar  *dss_x,
                     uchar  *dss_k,
                     uchar  *r,
                     uchar  *s,
                     uchar  *hash_result)

{
    int  status = SUCCESS;          /* function return status */
	ord r_temp[DSS_LENGTH_MAX];      /* r intermidiate value */
    ord k_inverse[SHA_LENGTH+1]; 
	ord temp[SHA_LENGTH+1];            /* intermidiate values    */
	ord *dss_p_a;
	ord *dss_g_a;
	ord *dss_q_a;
	ord *dss_x_a;
	ord *dss_k_a;
	ord *hash_result_a;
	ord *r_a;
	ord *s_a;

    if ( (dss_p_bytes < DSS_LENGTH_MIN) ||     /* less than minimal */
		(dss_p_bytes > DSS_LENGTH_MAX) )      /* more than maximal */
    {
		status = ERR_DSS_LEN;           /* invalid length for dss_p */
        return status;
    }
    if ( dss_p_bytes & 0x07 )          /* not multiple 8 (64 bit)*/
    {
	    status = ERR_DSS_LEN;          /* invalid length for dss_p */
	    return status;
    }

    if (DataOrder)
    {
        if (dss_p)
          BigSwap(dss_p, dss_p_bytes);
        if (dss_q)
          BigSwap(dss_q, SHA_LENGTH);
        if (dss_g)
          BigSwap(dss_g, dss_p_bytes);
        BigSwap(dss_x, SHA_LENGTH);
        BigSwap(dss_k, SHA_LENGTH);
        BigSwap(hash_result, SHA_LENGTH);
    }

    DSS_G_ALIGN_CALLOC_COPY(dss_g, dss_g_a, dss_p_bytes);  /*TKL01101*/
    DSS_P_ALIGN_CALLOC_COPY(dss_p, dss_p_a, dss_p_bytes);  /*TKL01101*/
    DSS_Q_ALIGN_CALLOC_COPY(dss_q, dss_q_a, SHA_LENGTH);   /*TKL01101*/

 ALIGN_CALLOC_COPY(dss_x, dss_x_a, SHA_LENGTH);
 ALIGN_CALLOC_COPY(dss_k, dss_k_a, SHA_LENGTH);
 ALIGN_CALLOC_COPY(hash_result, hash_result_a, SHA_LENGTH);
 ALIGN_CALLOC(r, r_a, SHA_LENGTH);
 ALIGN_CALLOC(s, s_a, SHA_LENGTH);
 if ( status !=  SUCCESS )
 {
    if( dss_p_a )
	  DSS_ALIGN_FREE(dss_p_a,dss_p);
    if( dss_g_a )
	  DSS_ALIGN_FREE(dss_g_a,dss_g);
    if( dss_q_a )
	  DSS_ALIGN_FREE(dss_q_a,dss_q);
    if( dss_x_a )
	{
	  memset ( dss_x_a , 0 , SHA_LENGTH );
	  ALIGN_FREE(dss_x_a);
    }
	if( dss_k_a )
	{
	  ALIGN_FREE(dss_k_a);
    }
	if( hash_result_a )
	{
	  ALIGN_FREE(hash_result_a);
	}
	if( r_a )
	{
	  ALIGN_COPY_FREE(r_a,r,SHA_LENGTH);
	}
	if( s_a )
	{
	  ALIGN_COPY_FREE(s_a,s,SHA_LENGTH);
	}
    if (DataOrder)
    {
        if (dss_p)
          BigSwap(dss_p, dss_p_bytes);
        if (dss_q)
          BigSwap(dss_q, SHA_LENGTH);
        if (dss_g)
          BigSwap(dss_g, dss_p_bytes);
        BigSwap(dss_x, SHA_LENGTH);
        BigSwap(dss_k, SHA_LENGTH);
        BigSwap(hash_result, SHA_LENGTH);
    }
	return status;     /* ERR_ALLOC   insufficient memory */
 }

/* Compute DSS r value */
 BEGIN_PROCESSING
	 if (( status = Expo ( dss_p_bytes,
						   dss_g_a,
						   SHA_LENGTH,   /* r_temp=(dss_g^dss_k)mod(dss_p)*/
						   dss_k_a,
						   dss_p_bytes,
						   dss_p_a,
						   r_temp )) != SUCCESS )
	 {
							  ERROR_BREAK;
	 }
	 if (( status = PartReduct ( dss_p_bytes,
	 							 r_temp,
	 							 SHA_LENGTH,      /* r=(r_temp)mod(dss_q) */
	 						     dss_q_a,
	 						     r_a )) != SUCCESS )
	 {
				ERROR_BREAK;
	 }
/* Compute k modulo inverse value */
	 if (( status = Inverse( SHA_LENGTH,  /* k_inverse=dss_k^(-1)mod(dss_q)*/
							  dss_k_a,
							  SHA_LENGTH,
							  dss_q_a,
							  k_inverse )) != SUCCESS  )
	 {
			ERROR_BREAK;
	 }
/* Compute DSS s value */
	 if (( status = Mul ( SHA_LENGTH,    /* temp=(dss_x*r)mod(dss_q) */
							dss_x_a,
							SHA_LENGTH,
							r_a,
						    SHA_LENGTH,
							dss_q_a,
							temp )) != SUCCESS )
	 {
			 ERROR_BREAK;
	 }

	/* Add( temp, hash_result_a,
		 SHA_LENGTH, dss_q_a, temp );   temp=(temp+hash_result)mod(dss_q)*/

	 Add( temp, hash_result_a,
		 SHA_LENGTH, dss_q_a );
	 if (( status = Mul ( SHA_LENGTH, /* s=(temp*k_inverse)mod(dss_q) */
						  temp,
					      SHA_LENGTH,
						  k_inverse,
						  SHA_LENGTH,
						  dss_q_a,
						  s_a )) != SUCCESS )
	 {
				ERROR_BREAK;
	 }
  END_PROCESSING

  DSS_ALIGN_FREE(dss_p_a,dss_p);  /*TKL01101*/
  DSS_ALIGN_FREE(dss_g_a,dss_g);  /*TKL01101*/
  DSS_ALIGN_FREE(dss_q_a,dss_q);  /*TKL01101*/
  ALIGN_FREE(dss_x_a);
  ALIGN_FREE(dss_k_a);
  ALIGN_FREE(hash_result_a);
  ALIGN_COPY_FREE(r_a,r,SHA_LENGTH);
  ALIGN_COPY_FREE(s_a,s,SHA_LENGTH);

    if (DataOrder)
    {
        if (dss_p)
          BigSwap(dss_p, dss_p_bytes);
        if (dss_q)
          BigSwap(dss_q, SHA_LENGTH);
        if (dss_g)
          BigSwap(dss_g, dss_p_bytes);
        BigSwap(dss_x, SHA_LENGTH);
        BigSwap(dss_k, SHA_LENGTH);
        BigSwap(hash_result, SHA_LENGTH);
        BigSwap(r, SHA_LENGTH);
        BigSwap(s, SHA_LENGTH);
    }

  return status;
}

/****************************************************************************
*  NAME:  int VerDSSSignature( u_int16_t dss_p_bytes,
*                              uchar  *dss_p,
*                              uchar  *dss_q,
*                              uchar  *dss_g,
*                              uchar  *dss_y,
*                              uchar  *r,
*                              uchar  *s,
*                              uchar  *hash_result)
*
*  DESCRIPTION:  Verify a DSS Signature
*
*  INPUTS:
*      PARAMETERS:
*          u_int16_t dss_p_bytes      Length of dss_p
*          uchar *dss_p            Pointer to p prime
*          uchar *dss_q            Pointer to q prime
*          uchar *dss_g            Pointer to g
*          uchar *dss_y            Pointer to public number
*          uchar *hash_result      Pointer to message hashing result
*  OUTPUT:
*      PARAMETERS:
*
*      RETURN:
*          SUCCESS                  No errors
*          ERR_SIGNATURE            Signature is not valid
*          ERR_DSS_LEN              Invalid length for dss_p
*          ERR_ALLOC                Insufficient memory
*
*  REVISION HISTORY:
*
*  24 Sep 94   KPZ     Initial release
*  14 Oct 94   GKL     Second version (big endian support)
*  26 Oct 94   GKL     (alignment for big endian support & ERR_ALLOC)
*  08 Dec 94   GKL     Changed Expo call
*  21 Aug 96   AAB	  DoubleExpo call
****************************************************************************/

int VerDSSSignature( u_int16_t dss_p_bytes,
					 uchar  *dss_p,
					 uchar  *dss_q,
					 uchar  *dss_g,
					 uchar  *dss_y,
					 uchar  *r,
				     uchar  *s,
				     uchar  *hash_result)
{
     int  status = SUCCESS;          /* function return status */
	 ord  w[(SHA_LENGTH / sizeof(ord)) + 1];
	 ord u1[SHA_LENGTH / sizeof(ord)];
	 ord u2[SHA_LENGTH / sizeof(ord)];
	 ord *v;
	 ord *dss_p_a;
	 ord *dss_g_a;
	 ord *dss_q_a;
	 ord *dss_y_a;
	 ord *hash_result_a;
	 ord *r_a;
	 ord *s_a;
	 if ( (dss_p_bytes < DSS_LENGTH_MIN) ||     /* less than minimal */
			(dss_p_bytes > DSS_LENGTH_MAX) )      /* more than maximal */
	 {
		 status = ERR_DSS_LEN;           /* invalid length for dss_p */
		 return status;
	 }
	 if ( dss_p_bytes & 0x07 )          /* not multiple 8 (64 bit)*/
	 {
	     status = ERR_DSS_LEN;          /* invalid length for dss_p */
	     return status;
	 }

    if (DataOrder)
    {
        if (dss_p)
          BigSwap(dss_p, dss_p_bytes);
        if (dss_q)
          BigSwap(dss_q, SHA_LENGTH);
        if (dss_g)
          BigSwap(dss_g, dss_p_bytes);
        BigSwap(dss_y, dss_p_bytes);
        BigSwap(hash_result, SHA_LENGTH);
        BigSwap(r, SHA_LENGTH);
        BigSwap(s, SHA_LENGTH);
    }

	 DSS_P_ALIGN_CALLOC_COPY(dss_p, dss_p_a, dss_p_bytes);  /*TKL01101*/
	 DSS_Q_ALIGN_CALLOC_COPY(dss_q, dss_q_a, SHA_LENGTH);   /*TKL01101*/
	 DSS_G_ALIGN_CALLOC_COPY(dss_g, dss_g_a, dss_p_bytes);  /*TKL01101*/

	 ALIGN_CALLOC_COPY(dss_y, dss_y_a, dss_p_bytes);
	 ALIGN_CALLOC_COPY(hash_result, hash_result_a, SHA_LENGTH);
	 ALIGN_CALLOC_COPY(r, r_a, SHA_LENGTH);
	 ALIGN_CALLOC_COPY(s, s_a, SHA_LENGTH);
	 CALLOC(v,ord,dss_p_bytes);

	 if ( status !=  SUCCESS )
	 {
		 if( dss_p_a )
	     {
			DSS_ALIGN_FREE(dss_p_a,dss_p);
		 }
		 if( dss_g_a )
	     {
			DSS_ALIGN_FREE(dss_g_a,dss_g);
		 }
		 if ( dss_q_a )
         {
			DSS_ALIGN_FREE(dss_q_a,dss_q);
		 }
		 if( dss_y_a )
	     {
		 	 ALIGN_FREE(dss_y_a);
	     }
		 if( hash_result_a )
	     {
		 	 ALIGN_FREE(hash_result_a);
		 }
		 if( r_a )
		 {
		 	 ALIGN_FREE(r_a);
		 }
		 if( s_a )
		 {
		 	 ALIGN_FREE(s_a);
		 }
		if( v )
        {
		    free ( v );
	    }
        if (DataOrder)
        {
            if (dss_p)
                BigSwap(dss_p, dss_p_bytes);
            if (dss_q)
                BigSwap(dss_q, SHA_LENGTH);
            if (dss_g)
                BigSwap(dss_g, dss_p_bytes);
            BigSwap(dss_y, dss_p_bytes);
            BigSwap(hash_result, SHA_LENGTH);
            BigSwap(r, SHA_LENGTH);
            BigSwap(s, SHA_LENGTH);
        }

		return status;     /* ERR_ALLOC   insufficient memory */
	 }

 BEGIN_PROCESSING
    if (( status = Inverse( SHA_LENGTH,     /* w=dss_k^(-1)mod(dss_q)*/
                            s_a,
                            SHA_LENGTH,
                            dss_q_a,
                            w )) !=SUCCESS  )
    {
        ERROR_BREAK;
    }
    if (( status = Mul ( SHA_LENGTH,    /* u1=(hash_result_*w)mod(dss_q) */
                         hash_result_a,
                   SHA_LENGTH,
                         w,
                  SHA_LENGTH,
                         dss_q_a,
                    u1 )) != SUCCESS )
    {
        ERROR_BREAK;
    }
    if (( status = Mul ( SHA_LENGTH,    /* u2=(r*w)mod(dss_q) */
                         r_a,
                         SHA_LENGTH,
                         w,
                         SHA_LENGTH,
                         dss_q_a,
							  u2 )) != SUCCESS )
	 {
		  ERROR_BREAK;
	 }
	 /* v = dss_g_a^u1*dss_y_a^u2 moddss_p_a */
	if((status = DoubleExpo( dss_p_bytes, dss_g_a,
									 SHA_LENGTH, u1,
									 dss_p_bytes, dss_y_a,
									 SHA_LENGTH, u2,
									 dss_p_bytes, dss_p_a, v)) != SUCCESS )
	{

		  ERROR_BREAK;
	}
	if (( status = PartReduct ( dss_p_bytes,         /*v = v mod(dss_q)*/
																			  v,
																				  SHA_LENGTH,
																				 dss_q_a,
																					 v )) != SUCCESS )
		 {
					ERROR_BREAK;
	 }

		 if (( status = memcmp( r_a, v, SHA_LENGTH)) != 0)   /*if v=r sign valid */
		{
					status = ERR_SIGNATURE;             /* signature is not valid */
                ERROR_BREAK;
    }
 END_PROCESSING
   free ( v );
  DSS_ALIGN_FREE(dss_p_a,dss_p);  /*TKL01101*/
  DSS_ALIGN_FREE(dss_g_a,dss_g);  /*TKL01101*/
  DSS_ALIGN_FREE(dss_q_a,dss_q);  /*TKL01101*/
  ALIGN_FREE(dss_y_a);
  ALIGN_FREE(hash_result_a);
  ALIGN_FREE(r_a);
  ALIGN_FREE(s_a);

  if (DataOrder)
  {
      if (dss_p)
          BigSwap(dss_p, dss_p_bytes);
      if (dss_q)
          BigSwap(dss_q, SHA_LENGTH);
      if (dss_g)
          BigSwap(dss_g, dss_p_bytes);
      BigSwap(dss_y, dss_p_bytes);
      BigSwap(hash_result, SHA_LENGTH);
      BigSwap(r, SHA_LENGTH);
      BigSwap(s, SHA_LENGTH);
  }

  return status;
}


/****************************************************************************
*  NAME:    int GenDSSKey( u_int16_t dss_p_bytes,
*                          uchar  *dss_p,
*                          uchar  *dss_q,
*                          uchar  *dss_g,
*                          uchar  *dss_x,
*                          uchar  *dss_y,
*                          uchar  *XKEY )
*
*
*  DESCRIPTION:  Compute DSS public/secret number pair.
*
*  INPUTS:
*      PARAMETERS:
*          u_int16_t dss_p_bytes     Length of modulo
*          uchar *dss_p           Pointer to modulo
*          uchar *dss_q           Pointer to modulo
*          uchar *dss_g           Pointer to public key
*          uchar *XKEY            Pointer to user supplied random number
*
*
*  OUTPUT:
*      PARAMETERS:
*          uchar *dss_x           Pointer to secret key
*          uchar *dss_y           Pointer to public key
*          uchar *XKEY            Pointer to updated number
*
*      RETURN:
*          SUCCESS               No errors
*          ERR_INPUT_LEN         Invalid length for input data
*          ERR_DATA              Generic data error
*          ERR_ALLOC             Insufficient memory
*  REVISION HISTORY:
*
*  24 Sep 94   KPZ     Initial release
*  14 Oct 94   GKL     Second version (big endian support)
*  26 Oct 94   GKL     (alignment for big endian support & ERR_ALLOC)
*  08 Dec 94   GKL     Changed Expo call
*
****************************************************************************/

int GenDSSKey( u_int16_t dss_p_bytes,
               uchar  *dss_p,
               uchar  *dss_q,
               uchar  *dss_g,
               uchar  *dss_x,
               uchar  *dss_y,
               uchar  *XKEY )
{

	 int  status = SUCCESS;          /* function return status */
	 SHA_context hash_context;       /* SHA context structure */
	 uchar M[DSS_LENGTH_MIN];        /* message block */
	 ord *dss_p_a;
	 ord *dss_q_a;
	 ord *dss_g_a;
	 ord *dss_x_a;
	 ord *dss_y_a;
	 ord *XKEY_a;
	 if ( (dss_p_bytes < DSS_LENGTH_MIN) ||     /* less than minimal */
	    (dss_p_bytes > DSS_LENGTH_MAX) )       /* more than maximal */
     {
			  status = ERR_DSS_LEN;           /* invalid length for dss_p */
							 return status;
	 }
	 if ( dss_p_bytes & 0x07 )          /* not multiple 8 (64 bit)*/
	 {
		status = ERR_DSS_LEN;          /* invalid length for dss_p */
	    return status;
	 }
     if (DataOrder)
     {
         if (dss_p)
             BigSwap(dss_p, dss_p_bytes);
         if (dss_q)
             BigSwap(dss_q, SHA_LENGTH);
         if (dss_g)
             BigSwap(dss_g, dss_p_bytes);
     }

	 DSS_P_ALIGN_CALLOC_COPY(dss_p, dss_p_a, dss_p_bytes);  /*TKL01101*/
	 DSS_G_ALIGN_CALLOC_COPY(dss_g, dss_g_a, dss_p_bytes);  /*TKL01101*/
	 DSS_Q_ALIGN_CALLOC_COPY(dss_q, dss_q_a, SHA_LENGTH);   /*TKL01101*/
	 ALIGN_CALLOC(dss_x, dss_x_a, SHA_LENGTH);
	 ALIGN_CALLOC(dss_y, dss_y_a, dss_p_bytes);
	 if ( status !=  SUCCESS )
	 {
		if( dss_p_a )
		 DSS_ALIGN_FREE(dss_p_a,dss_p);
		if( dss_g_a )
		 DSS_ALIGN_FREE(dss_g_a,dss_g);
		if( dss_q_a )
		 DSS_ALIGN_FREE(dss_q_a,dss_q);
		if( dss_x_a )
	    {
		     memset(dss_x_a, 0, SHA_LENGTH );
		     ALIGN_COPY_FREE(dss_x_a,dss_x,SHA_LENGTH);
	    }
		if( dss_y_a )
	    {
			 ALIGN_COPY_FREE(dss_y_a,dss_y,dss_p_bytes);
	    }

        if (DataOrder)
        {
          if (dss_p)
             BigSwap(dss_p, dss_p_bytes);
          if (dss_q)
             BigSwap(dss_q, SHA_LENGTH);
          if (dss_g)
             BigSwap(dss_g, dss_p_bytes);
        }
		return status;     /* ERR_ALLOC   insufficient memory */
	 }

  BEGIN_PROCESSING
	 SHAInit ( &hash_context );
	 memcpy( M, XKEY, SHA_LENGTH);
	 memset( M + SHA_LENGTH, 0, DSS_LENGTH_MIN - SHA_LENGTH );
	  if ( (status = SHAUpdate( &hash_context, M, DSS_LENGTH_MIN ))
			!= SUCCESS )
	 {
		 ERROR_BREAK;
	 }
	 if ( (status = MySHAFinal (&hash_context, (uchar *)dss_x_a)) != SUCCESS )
	 {
		ERROR_BREAK;
	 }
	 if (( status = PartReduct ( SHA_LENGTH,         /* dss_x = dss_x mod(dss_q)*/
										  dss_x_a,
										  SHA_LENGTH,
										  dss_q_a,
										  dss_x_a )) != SUCCESS )
	 {
		  ERROR_BREAK;
	 }

     BigSwap(XKEY, SHA_LENGTH);
	 ALIGN_CALLOC_COPY(XKEY, XKEY_a, SHA_LENGTH);
	 if ( status !=  SUCCESS )
	 {
		if( XKEY_a )
	    {
			 ALIGN_COPY_FREE(XKEY_a,XKEY,SHA_LENGTH);
             BigSwap(XKEY, SHA_LENGTH);
             return status;     /* ERR_ALLOC   insufficient memory */
        }
     }

	 Sum_Q((ord*)XKEY_a, 1, SHA_LENGTH / sizeof (ord) );

	 Sum_big( XKEY_a, dss_x_a,  /* XKEY=XKEY+dss_x */
			 XKEY_a, SHA_LENGTH / sizeof(ord) );

     ALIGN_COPY_FREE(XKEY_a,XKEY,SHA_LENGTH);
     BigSwap(XKEY, SHA_LENGTH);

	 if (( status = Expo ( dss_p_bytes,     /*dss_y = g^dss_x mod(dss_p)*/
								 dss_g_a,
								SHA_LENGTH,
															dss_x_a,
								dss_p_bytes,
                    dss_p_a,
																dss_y_a)) != SUCCESS ) /*TKL00601*/
    {
           ERROR_BREAK;
    }
  END_PROCESSING

    DSS_ALIGN_FREE(dss_p_a,dss_p);  /*TKL01101*/
    DSS_ALIGN_FREE(dss_g_a,dss_g);  /*TKL01101*/
    DSS_ALIGN_FREE(dss_q_a,dss_q);  /*TKL01101*/
    ALIGN_COPY_FREE(dss_x_a,dss_x,SHA_LENGTH);
    ALIGN_COPY_FREE(dss_y_a,dss_y,dss_p_bytes);
    if (DataOrder)
    {
         if (dss_p)
             BigSwap(dss_p, dss_p_bytes);
         if (dss_q)
             BigSwap(dss_q, SHA_LENGTH);
         if (dss_g)
             BigSwap(dss_g, dss_p_bytes);
         BigSwap(dss_x, SHA_LENGTH);
         BigSwap(dss_y, dss_p_bytes);
    }

    return status;
}



/****************************************************************************
*  NAME:    int GenDSSNumber( uchar *dss_k,
*                             uchar *dss_q,
*                             uchar *KKEY )
*
*  DESCRIPTION:  Generate secret number
*
*  INPUTS:
*      PARAMETERS:
*          uchar *KKEY      Pointer to input random number
*          uchar *dss_q     Pointer to modulo
*
*
*  OUTPUT:
*      PARAMETERS:
*          uchar *dss_x      Pointer to secret number
*          uchar *KKEY       Pointer to updated KKEY
*
*      RETURN:
*          SUCCESS           No errors
*          ERR_DATA          Generic data error
*          ERR_ALLOC         Insufficient memory
*  REVISION HISTORY:
*
*  24 Sep 94   KPZ     Initial release
*  14 Oct 94   GKL     Second version (big endian support)
*  26 Oct 94   GKL     (alignment for big endian support & ERR_ALLOC)
*
****************************************************************************/

int GenDSSNumber( uchar *dss_k,
					  uchar *dss_q,
						uchar *KKEY )
{

 int  status = SUCCESS;        /* function return status */
	 ord *dss_k_a;
	 ord *dss_q_a;
	 ord *KKEY_a;
	 SHA_context hash_context;     /* SHA context structure*/
	 uchar M[DSS_LENGTH_MIN];      /* message block */

     if (DataOrder)
     {
         if (dss_q)
             BigSwap(dss_q, SHA_LENGTH);
     }

	 DSS_Q_ALIGN_CALLOC_COPY(dss_q, dss_q_a, SHA_LENGTH);   /*TKL01101*/
	 ALIGN_CALLOC(dss_k, dss_k_a, SHA_LENGTH);
	 if ( status !=  SUCCESS )
	 {
		 if( dss_q_a )
		     DSS_ALIGN_FREE(dss_q_a,dss_q);
		 if( dss_k_a )
	     {
		 	 ALIGN_COPY_FREE(dss_k_a,dss_k,SHA_LENGTH);
	     }
         if (DataOrder)
         {
            if (dss_q)
                BigSwap(dss_q, SHA_LENGTH);
         }
         return status;     /* ERR_ALLOC   insufficient memory */
	 }

  BEGIN_PROCESSING
	 SHAInitK ( &hash_context );
	 memcpy( M, KKEY, SHA_LENGTH);
	 memset( M + SHA_LENGTH, 0, DSS_LENGTH_MIN - SHA_LENGTH );
	 if ( (status = SHAUpdate( &hash_context, M, DSS_LENGTH_MIN ))
			!= SUCCESS )
	 {
		 ERROR_BREAK;
	 }
	 if ( (status = MySHAFinal (&hash_context, (uchar *)dss_k_a)) != SUCCESS )
	 {
		 ERROR_BREAK;
	 }
	 if (( status = PartReduct ( SHA_LENGTH,         /* dss_k = dss_k mod(dss_q)*/
										  dss_k_a,
										  SHA_LENGTH,
																				  dss_q_a,
										  dss_k_a )) != SUCCESS )
	 {
		  ERROR_BREAK;
	 }
     BigSwap(KKEY, SHA_LENGTH);
	 ALIGN_CALLOC_COPY(KKEY, KKEY_a, SHA_LENGTH );
	 if ( status !=  SUCCESS )
	 {
		 if ( KKEY_a )
	     {
		 	 ALIGN_COPY_FREE(KKEY_a,KKEY,SHA_LENGTH);
             BigSwap(KKEY, SHA_LENGTH);
		 }
         return status;     /* ERR_ALLOC   insufficient memory */
	 }

	 Sum_Q( KKEY_a, 1, SHA_LENGTH / sizeof(ord));

	 Sum_big( KKEY_a, dss_k_a,                    /* KKEY=KKEY+dss_k*/
		  KKEY_a, SHA_LENGTH  / sizeof(ord) );

	 ALIGN_COPY_FREE(KKEY_a,KKEY,SHA_LENGTH);
     BigSwap(KKEY, SHA_LENGTH);

  END_PROCESSING

	 DSS_ALIGN_FREE(dss_q_a,dss_q);  /*TKL01101*/

	 ALIGN_COPY_FREE(dss_k_a,dss_k,SHA_LENGTH);
     if (DataOrder)
     {
         if (dss_q)
             BigSwap(dss_q, SHA_LENGTH);
         BigSwap(dss_k, SHA_LENGTH);
     }

	 return status;
}


/****************************************************************************
*  NAME: int GenDSSParameters( u_int16_t dss_p_bytes,
*                               uchar  *dss_p,
*                               uchar  *dss_q,
*                               uchar *dss_g,
*                               uchar  *RVAL,
*                                                 YIELD_context *yield_cont )
*
*  DESCRIPTION: Generate DSS Common Parameters
*
*  INPUTS:
*      PARAMETERS:
*            u_int16_t dss_p_bytes    Number of bytes in dss_p
*            uchar  *RVAL          Pointer to user supplied random number
*            YIELD_context *yield_cont  Pointer to yield_cont structure (NULL if not used)
*  OUTPUT:
*      PARAMETERS:
*            uchar *dss_p          Pointer to N-byte prime number
*            uchar *dss_q          Pointer to SHA_LENGTH prime number
*            uchar *dss_g          Pointer to N-byte number
*      RETURN:
*          SUCCESS              No errors
*          ERR_INPUT_LEN        Invalid length for input data(zero bytes)
*          ERR_DSS_LEN;         Invalid length for dss_p
*          ERR_ALLOC            Insufficient memory
*  REVISION HISTORY:
*
*  24 Sep 94   KPZ     Initial release
*  14 Oct 94   GKL     Second version (big endian support)
*  26 Oct 94   GKL     (alignment for big endian support & ERR_ALLOC)
*  08 Dec 94   GKL      Added YIELD_context
*
****************************************************************************/

 int GenDSSParameters( u_int16_t dss_p_bytes,
					 uchar  *dss_p,
					uchar  *dss_q,
				uchar  *dss_g,
				uchar  *RVAL,
			 YIELD_context *yield_cont ) /*TKL00701*/
{
	 int status = SUCCESS;      /* function return status */
	 ord seed[SHA_LENGTH / sizeof (ord)];
	 ord u[SHA_LENGTH / sizeof (ord)];            /* work buffers */
	 ord u1[SHA_LENGTH / sizeof (ord)];
	 ord  *dss_p_a;
	 ord  *dss_q_a;
	 ord  *dss_g_a;
	 ord  *RVAL_a;
	 ord ofone[SHA_LENGTH / sizeof(ord)];
	 ord dss_q2[SHA_LENGTH / sizeof(ord) + 1];  /* dss_q2 = 2 * q */
	 ord v[SHA_LENGTH / sizeof(ord)];
	 ord *w, *c, *c1, *one, *e;           /* Pointers to work buffers */
	 u_int16_t i, n, count, offset, k;          /* counters */
	 u_int16_t dss_p_longs;                 /* number of longs */
	 if ( dss_p_bytes == 0 )    /* invalid length for input data (zero bytes) */
	 {
		  status = ERR_INPUT_LEN;
          return status;
	 }
	 if ( (dss_p_bytes < DSS_LENGTH_MIN) ||     /* less than minimal */
		  (dss_p_bytes > DSS_LENGTH_MAX) )      /* more than maximal */
	 {
	      status = ERR_DSS_LEN;           /* invalid length for dss_p */
          return status;
     }
	 if ( dss_p_bytes & 0x07 )          /* not multiple 4 (64 bit)*/
     {
		 status = ERR_DSS_LEN;          /* invalid length for dss_p */
	     return status;
	 }

	 n = (u_int16_t) (dss_p_bytes / SHA_LENGTH);       /* SHA passes count */
	 dss_p_longs = (u_int16_t) (dss_p_bytes / sizeof(ord));  /* number of long in dss_p */
	 CALLOC(w, ord, (n + 1) * SHA_LENGTH);
	 CALLOC(c, ord, dss_p_bytes);
	 CALLOC(c1, ord, dss_p_bytes);
	 CALLOC(one, ord, dss_p_bytes);
	 CALLOC(e,ord, dss_p_bytes - SHA_LENGTH + sizeof(ord));
	 ALIGN_CALLOC_MOVE(RVAL, RVAL_a, SHA_LENGTH);
	 ALIGN_CALLOC(dss_p, dss_p_a, dss_p_bytes);
	 ALIGN_CALLOC(dss_q, dss_q_a, SHA_LENGTH);
	 ALIGN_CALLOC(dss_g, dss_g_a, dss_p_bytes);
	 if ( status !=  SUCCESS )
	 {
	    if( e )
			free ( e );
	    if( one )
			free ( one );
	    if( c )
			free ( c );
	    if( w )
			free ( w );
	    if( c1 )
		    free ( c1 );
	    if( dss_p_a )
	    {
		    ALIGN_COPY_FREE(dss_p_a, dss_p, dss_p_bytes);
	    }
		if( dss_q_a )
	    {
		    ALIGN_COPY_FREE(dss_q_a, dss_q, SHA_LENGTH);
	    }
		if( dss_g_a )
	    {
			ALIGN_COPY_FREE(dss_g_a, dss_g, dss_p_bytes);
	    }
		if( RVAL_a )
	    {
			ALIGN_MOVE_FREE(RVAL_a, RVAL, SHA_LENGTH);
		}
        return status;     /* ERR_ALLOC   insufficient memory */
	  }
	 one[0] = 1;
	 BEGIN_PROCESSING   /* Generate DSS Common Parameters */

			  BEGIN_LOOP   /* Generate prime & dss_p */  /*TKL00501*/
					/* generate prime number of length 160-bit */
					do
					{
						 MyGenRand( SHA_LENGTH, seed, RVAL_a);  /* generate random number */
										/* compute message digest from seed */
						 if ( (status = MySHA((uchar *)seed, SHA_LENGTH, (uchar *)u)) != SUCCESS )
						 {
							  break;  /* error */
						 }
						 memcpy(ofone, seed,SHA_LENGTH);
						 Sum_Q( ofone, 1, SHA_LENGTH / sizeof(ord) );
					/* compute message digest from seed */
						 if ( (status = MySHA( (uchar *)ofone, SHA_LENGTH,(uchar *)dss_q_a)) != SUCCESS )
						 {
													break;  /* error */
						 }

						 for ( i = 0; i < (SHA_LENGTH / sizeof(ord)); i++ )  /* dss_q = dss_q ^ u */
											  {
							  dss_q_a[i] = dss_q_a[i] ^ u[i];
						 }
					  /* set least and most significant bits */
						 dss_q_a[SHA_LENGTH / sizeof(ord) - 1] |= ((ord)1 << (BITS_COUNT-1));
									 dss_q_a[0] |= 0x01;
							} while ( VerPrime( SHA_LENGTH, dss_q_a, TEST_COUNT, RVAL_a, yield_cont) /*TKL00701*/
																 != SUCCESS );   /* perform a robust primality test */
							if (status != SUCCESS )
								 {
									  ERROR_BREAK;
							 }
						  /* dss_q2 = 2 * dss_q */
							 memcpy( dss_q2, dss_q_a, SHA_LENGTH );
							 dss_q2[SHA_LENGTH / sizeof(ord)] = 0;
							 LShiftL_big( dss_q2, SHA_LENGTH  / sizeof(ord) +1, 1 );
							 count = 0;
							 offset = 2;
							 memset( ofone, 0, SHA_LENGTH );
								 do   /* find dss_p */
									{
									 /* generate random number by dss_p bytes */
										  for ( k = 0;  k <= n; k++ )
									  {
													ofone[0] = offset + k;
													/* v = ofone + seed */
												 Sum_big( seed, ofone, v, SHA_LENGTH / sizeof(ord) );
												if ( (status = MySHA ( (uchar *)v, SHA_LENGTH,
																			 (uchar *)( w + (SHA_LENGTH / sizeof(ord)) * k )))
																					!= SUCCESS ) /* compute message digest */
													{
															  break; /* error */
												  }
										 }
										 if (status != SUCCESS )
											{
												  break; /* error */
										 }
									 /* set most significant bit */
									  w[dss_p_longs - 1] |= ((ord)1 << (BITS_COUNT-1));
										 memcpy( c, w, dss_p_bytes);
								  /* c1 = c mod(dss_q2) */
											  if( (status = PartReduct( dss_p_bytes, c,
																												 SHA_LENGTH + sizeof(ord),
																													dss_q2, c1)) != SUCCESS )
											{
													break; /* error */
										}
									 /* c1 = c1 - 1*/
											  Sub_big( c1, one, c1, dss_p_longs );
								 /* dss_p = w - c1 */
										 Sub_big( w, c1, dss_p_a, dss_p_longs );
											if ( dss_p_a[dss_p_bytes / sizeof(ord) - 1] >= (ord)((ord)1 << (BITS_COUNT-1)) )
										  {
													if ( VerPrime ( dss_p_bytes, dss_p_a, TEST_COUNT, RVAL_a, yield_cont) /*TKL00701*/
																							 == SUCCESS ) /* perform a robust primality test */
												  {
															  break;
														}
										 }
										 count++;
										 offset = (u_int16_t) (offset + n + 1);
								} while ( count < 4096);
								if (status != SUCCESS )
								 {
									  ERROR_BREAK;
							 }
							  if (count != 4096)          /*TKL00501*/
								{
									  BREAK;                    /*TKL00501*/
						  }
		 END_LOOP     /* Generate dss_p */   /*TKL00501*/

		  if (status != SUCCESS )
			{
				  ERROR_BREAK;
	  }
		 dss_p_a[0] -= 1;   /* dss_p = dss_p - 1 */
		if ( (status= DivRem (dss_p_bytes, dss_p_a, SHA_LENGTH, dss_q_a, u1,
																  e )) != SUCCESS )  /* e = dss_p / dss_q */
		  {
				  ERROR_BREAK;
	  }
		 dss_p_a[0] += 1;    /* dss_p = dss_p + 1 */

	  BEGIN_LOOP   /* Generate dss_g */   /*TKL00501*/
							MyGenRand( SHA_LENGTH, u, RVAL_a );  /*generate random number*/
					  u[SHA_LENGTH / sizeof(ord) - 1] &= ~((ord)1 << (BITS_COUNT-1));       /* u < dss_q */
						 if ( (status = Expo( SHA_LENGTH, u, (u_int16_t)(dss_p_bytes - SHA_LENGTH +
																			 sizeof(ord)), e, dss_p_bytes, dss_p_a, dss_g_a )) 
																					!= SUCCESS ) /* dss_g = e ^ u mod(dss_p) */
						  {
										 ERROR_BREAK;
						  }
							  if ( dss_g_a[0] == 1 )   /* check dss_g == 1 */
					  {
								  for ( i = 1; i < (dss_p_bytes / sizeof(ord)); i++ )
							{
										if ( dss_g_a[i] != 0 )
											 {
													break;
											 }
								}
							  if ( i == (dss_p_bytes / sizeof(ord)) )
								 {
										CONTINUE;
								}
						  }
							  BREAK;                                 /*TKL00501*/
		  END_LOOP   /* Generate dss_g */             /*TKL00501*/
	  END_PROCESSING   /* Generate DSS Common Parameters */
	free ( e );
	  free ( one );
	free ( c );
	  free ( w );
	  free ( c1 );
	 ALIGN_COPY_FREE(dss_p_a, dss_p, dss_p_bytes);
	ALIGN_COPY_FREE(dss_q_a, dss_q, SHA_LENGTH);
	 ALIGN_COPY_FREE(dss_g_a, dss_g, dss_p_bytes);
	ALIGN_MOVE_FREE(RVAL_a, RVAL, SHA_LENGTH);
    if (DataOrder)
    {
        BigSwap(dss_p, dss_p_bytes);
        BigSwap(dss_q, SHA_LENGTH);
        BigSwap(dss_g, dss_p_bytes);
    }
	return status;
}


/****************************************************************************
*  NAME: int GetDSSPQG(u_int16_t dss_p_bytes,
*                      uchar  *dss_p,
*                      uchar  *dss_q,
*                      uchar  *dss_g)
*
*  DESCRIPTION: Copy Cylink DSS P,Q,G numbers to *dss_p,*dss_q,*dss_g
*
*  INPUTS:
*      PARAMETERS:
*            u_int16_t dss_p_bytes    Number of bytes in dss_p
*            uchar *dss_p          Pointer to N-byte buffer
*            uchar *dss_q          Pointer to SHA_LENGTH-byte buffer
*            uchar *dss_g          Pointer to N-byte buffer
*  OUTPUT:
*      RETURN:
*          SUCCESS              No errors
*          ERR_DSS_LEN;         Invalid length for dss_p
*  REVISION HISTORY:
*
*  22 Apr 95   GKL     Initial release       LOG TKL01201
*
****************************************************************************/
int GetDSSPQG(u_int16_t dss_p_bytes,
              uchar  *dss_p,
              uchar  *dss_q,
              uchar  *dss_g)
{
  int  status = SUCCESS;          /* function return status */
  ord *dss_p_a;
  ord *dss_g_a;

  if ( (dss_p_bytes < DSS_LENGTH_MIN) ||     /* less than minimal */
       (dss_p_bytes > DSS_LENGTH_MAX) )      /* more than maximal */
  {
      status = ERR_DSS_LEN;           /* invalid length for dss_p */
      return status;
  }
  if ( dss_p_bytes & 0x07 )          /* not multiple 8 (64 bit)*/
  {
      status = ERR_DSS_LEN;          /* invalid length for dss_p */
      return status;
  }
  dss_p_a = &DSS_P_NUMBERS[DSS_NUM_INDEX[(dss_p_bytes-DSS_LENGTH_MIN)/LENGTH_STEP]];
  dss_g_a = &DSS_G_NUMBERS[DSS_NUM_INDEX[(dss_p_bytes-DSS_LENGTH_MIN)/LENGTH_STEP]];
  OrdByte(dss_p_a,dss_p_bytes,dss_p);
  OrdByte(dss_g_a,dss_p_bytes,dss_g);
  OrdByte(DSS_Q_NUMBER,SHA_LENGTH,dss_q);
  if (DataOrder)
  {
      BigSwap(dss_p, dss_p_bytes);
      BigSwap(dss_q, SHA_LENGTH);
      BigSwap(dss_g, dss_p_bytes);
  }

  return status;
}


