CHALLENG.C
/* 
 *  This is a part of the Microsoft Source Code Samples. 
 *  Copyright 1996 - 1998 Microsoft Corporation. 
 *  All rights reserved. 
 * 
 *  The following example demonstrates how license updates are made and 
 *  how the challenge mechanism is used at request and update times.  The 
 *  application developer should analyze the application to determine the 
 *  appropriate time to perform an update.  For example, this application 
 *  prints a message 20 times and the appropriate time to do an update 
 *  was decided to be done after every 5th message was printed. 
 */ 
 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include "windows.h" 
 
/* 
 * Include the MD4 header for computing the challenge. 
 */ 
#include "md4.h" 
 
/* 
 * Include the LSAPI header file. 
 */ 
#include "lsapi.h" 
 
/* 
 * Define the product name, product version, and publisher name for 
 * use with the licensing calls. 
 */ 
#define MYAPP_PRODUCT_NAME    "sample_product" 
#define MYAPP_PRODUCT_VERSION "2.0" 
#define MYAPP_PUBLISHER_NAME  "sample_publishers" 
 
 
/* 
 * Define the strings used to log a comment with the license system. 
 */ 
#define MYAPP_REQUEST_LOG_COMMENT "Comment for the LSRequest call" 
#define MYAPP_RELEASE_LOG_COMMENT "Comment for the LSRelease call" 
#define MYAPP_UPDATE_LOG_COMMENT  "Comment for the LSUpdate call" 
 
/* 
 * The digest length is defined to be 16 according to the LSAPI spec. 
 */ 
#define DIGESTLEN                16 
 
/* 
 * Define the number of secrets 
 */ 
#define NUMBER_OF_SECRETS         4 
 
/* 
 * Define macros to indicate to the challenge routines whether we are 
 * doing an LSRequest or an LSUpdate 
 */ 
#define LS_REQUEST 0 
#define LS_UPDATE  1 
 
/* 
 * Global variable used to determine if we are on a little endian machine 
 * or not. 
 */ 
int          littleEndian = 0; 
 
 
/* 
 * Forward declarations. 
 */ 
void PrintErrors( LS_HANDLE handle, LS_STATUS_CODE errorCode ); 
LS_STATUS_CODE getChallenge( 
      LS_CHALLENGE    FAR *challenge, 
      LS_STR          FAR *theString, 
      LS_ULONG        unitsConsumed, 
      LS_ULONG        unitsReserved, 
      LS_STR          FAR *logcomment, 
      LS_ULONG        R, 
      LS_ULONG        X, 
      LS_ULONG        typeOfCall); 
int checkChallenge( 
      LS_CHALLENGE    FAR *challenge, 
      LS_STR          FAR *theString, 
      LS_ULONG        unitsConsumed , 
      LS_ULONG        unitsReserved , 
      LS_STR          FAR *logComment , 
      LS_ULONG        unitsGranted , 
      LS_STATUS_CODE  status , 
      LS_ULONG        R, 
      LS_ULONG        X, 
      LS_ULONG        typeOfCall); 
void swap( LS_STR FAR *byte1, LS_STR FAR *byte2 ); 
int PlaceInt( char FAR *buffer, LS_ULONG value ); 
 
 
 
 
 
__cdecl main() 
{ 
   LS_STATUS_CODE     status; 
   unsigned long      unitsGranted = 0; 
   LS_CHALLENGE       challenge; 
   LS_HANDLE          licenseHandle = (LS_HANDLE) NULL; 
   LS_ULONG           R; 
   LS_ULONG           X; 
   int                i; 
   int                endianTester = 1 ; 
   LS_STR             challengeString[100]; 
   char               FAR *endianChar = NULL; 
   LS_ULONG           Secrets[4] = { 0x7778797A, 0x31323334, 1633837924, 926431536 }; 
 
   /************ the following code is for the LSAPI beta only! ************/ 
   char               szProviderPath[ MAX_PATH ]; 
   UINT               nChars; 
 
   /* install if necessary */ 
   nChars = GetSystemDirectory( szProviderPath, sizeof( szProviderPath ) ); 
   if ( 0 == nChars ) 
   { 
      printf( "Can't get system directory, error %d.\n", GetLastError() ); 
   } 
 
   lstrcat( szProviderPath, "\\mslsp32.dll" ); 
   status = LSInstall( szProviderPath ); 
   if ( LS_SUCCESS != status ) 
   { 
      printf( "Cannot install LSAPI, error 0x%08lx.\n", status ); 
   } 
 
   /* add licenses for our product */ 
   status = LSLicenseUnitsSet( LS_ANY, 
                               MYAPP_PUBLISHER_NAME, 
                               MYAPP_PRODUCT_NAME, 
                               MYAPP_PRODUCT_VERSION, 
                               LS_LICENSE_TYPE_NODE, 
                               LS_NULL, 
                               1, 
                               sizeof( Secrets ) / sizeof( *Secrets ), 
                               Secrets ); 
   if ( LS_SUCCESS != status ) 
   { 
      printf( "Cannot install licenses, error 0x%lx.\n", status ); 
   } 
   /************ the above code is for the LSAPI beta only! ************/ 
 
   /* 
    * test endianness and set global variable by casting an int 
    * as bytes and look at the first byte 
    */ 
   endianChar = (char FAR *) &endianTester ; 
   if ( *endianChar ) 
        littleEndian = 1 ; 
 
   /* 
    * Calculate the string for the challenge on the LSRequest call. 
    */ 
   sprintf( challengeString, "%s%s%s%s%s", 
            "LSRequest", 
            LS_ANY, 
            MYAPP_PUBLISHER_NAME, 
            MYAPP_PRODUCT_NAME, 
            MYAPP_PRODUCT_VERSION ); 
 
   /* 
    * Pick the random number R and randomly pick a number for the index X 
    * between 1 and the number of secrets. 
    */ 
   srand( (unsigned)time( NULL ) ); 
   R = rand(); 
   X = (int)(NUMBER_OF_SECRETS * ((float)rand()/(float)RAND_MAX)) + 1; 
 
   /* 
    * Calculate the message digest for the challenge argument. 
    */ 
   status = getChallenge( 
         &challenge, 
         challengeString, 
         0,                    /* Unused argument for LSRequest call */ 
         LS_DEFAULT_UNITS, 
         MYAPP_REQUEST_LOG_COMMENT, 
         R, 
         X, 
         LS_REQUEST ); 
   if ( LS_SUCCESS != status ) 
      { 
      PrintErrors( licenseHandle, status ); 
      return(1); 
      } 
 
   /* 
    * Make the grant request call 
    */ 
   status = LSRequest( 
         (LS_STR FAR *) LS_ANY, 
         (LS_STR FAR *) MYAPP_PUBLISHER_NAME, 
         (LS_STR FAR *) MYAPP_PRODUCT_NAME, 
         (LS_STR FAR *) MYAPP_PRODUCT_VERSION, 
         LS_DEFAULT_UNITS, 
         (LS_STR FAR *) MYAPP_REQUEST_LOG_COMMENT, 
         &challenge, 
         &unitsGranted, 
         &licenseHandle); 
   if ( LS_SUCCESS != status ) 
      { 
      PrintErrors( licenseHandle, status ); 
      LSFreeHandle( licenseHandle ); 
      return(1); 
      } 
 
   /* 
    * Check whether the challenge succeeded. 
    */ 
   if ( !checkChallenge( 
         &challenge, 
         challengeString, 
         0,                    /* Unused argument for LSRequest call */ 
         LS_DEFAULT_UNITS, 
         MYAPP_REQUEST_LOG_COMMENT, 
         unitsGranted, 
         status, 
         R, 
         X, 
         LS_REQUEST ) ) 
      { 
      printf("Challenge failed!!!\n"); 
      /* 
       * Since we failed, we should still release the grant and free 
       * the license handle. 
       */ 
      status = LSRelease( licenseHandle, 
                          LS_DEFAULT_UNITS, 
                          (LS_STR FAR *) MYAPP_RELEASE_LOG_COMMENT); 
      if ( LS_SUCCESS != status ) 
         PrintErrors( licenseHandle, status ); 
 
      LSFreeHandle( licenseHandle ); 
      return(1); 
      } 
 
   /* 
    * Now we can start the application.  Print "Hello, World" 20 times 
    * performing an update after every 5th time. 
    */ 
   for ( i = 0; i<20; i++ ) 
      { 
      printf("Hello, World.\n"); 
 
      if ( 0 == i%5 ) 
         { 
         /* 
          * Calculate the challenge string for the challenge on 
          * the LSUpdate call. 
          */ 
         sprintf( challengeString, "%s", "LSUpdate"); 
 
         /* 
          * Pick the random number R using the process id for the seed 
          * to the random number generator.  Also randomly pick a number 
          * for the index X between 1 and NUMBER_OF_SECRETS. 
          */ 
         srand( (unsigned)time( NULL ) ); 
         R = rand(); 
         X = (int)(NUMBER_OF_SECRETS * ((float)rand()/(float)RAND_MAX)) + 1; 
 
         /* 
          * Calculate the message digest for the challenge argument. 
          */ 
         status = getChallenge( 
               &challenge, 
               challengeString, 
               LS_DEFAULT_UNITS, 
               LS_DEFAULT_UNITS, 
               MYAPP_UPDATE_LOG_COMMENT, 
               R, 
               X, 
               LS_UPDATE ); 
         if ( LS_SUCCESS != status ) 
            { 
            PrintErrors( licenseHandle, status ); 
            status = LSRelease( licenseHandle, 
                                LS_DEFAULT_UNITS, 
                                (LS_STR FAR *) MYAPP_RELEASE_LOG_COMMENT); 
            LSFreeHandle( licenseHandle ); 
            return(1); 
            } 
 
         /* 
          * Make the update call. 
          */ 
         status = LSUpdate( licenseHandle, 
                            LS_DEFAULT_UNITS, 
                            LS_DEFAULT_UNITS, 
                            (LS_STR FAR *) MYAPP_UPDATE_LOG_COMMENT, 
                            &challenge, 
                            &unitsGranted); 
         if ( LS_SUCCESS != status ) 
            { 
            PrintErrors( licenseHandle, status ); 
            status = LSRelease( licenseHandle, 
                                LS_DEFAULT_UNITS, 
                                (LS_STR FAR *) MYAPP_RELEASE_LOG_COMMENT); 
            LSFreeHandle( licenseHandle ); 
            return( 1 ); 
            } 
 
         /* 
          * Check whether the challenge succeeded. 
          */ 
         if ( !checkChallenge( 
               &challenge, 
               challengeString, 
               LS_DEFAULT_UNITS, 
               LS_DEFAULT_UNITS, 
               MYAPP_UPDATE_LOG_COMMENT, 
               unitsGranted, 
               status, 
               R, 
               X, 
               LS_UPDATE ) ) 
            { 
            printf("Challenge failed!!!\n"); 
            /* 
             * Release the grant. 
             */ 
            status = LSRelease( licenseHandle, 
                                LS_DEFAULT_UNITS, 
                                (LS_STR FAR *) MYAPP_RELEASE_LOG_COMMENT); 
            if ( LS_SUCCESS != status ) 
               PrintErrors( licenseHandle, status ); 
 
            LSFreeHandle( licenseHandle ); 
            return(1); 
            } 
         }  /* End of if ( 0 == i%5 ) */ 
 
      }  /* End of for loop. */ 
 
   /* 
    * Release the grant. 
    */ 
   status = LSRelease( licenseHandle, 
                       LS_DEFAULT_UNITS, 
                       (LS_STR FAR *) MYAPP_RELEASE_LOG_COMMENT); 
   if ( LS_SUCCESS != status ) 
      { 
      PrintErrors( licenseHandle, status ); 
      LSFreeHandle( licenseHandle ); 
      return( 1 ); 
      } 
 
   /* 
    * Free the license handle. 
    */ 
   LSFreeHandle( licenseHandle ); 
   return(0); 
} 
 
 
/* 
 * Print the error message. 
 */ 
void PrintErrors( LS_HANDLE handle, LS_STATUS_CODE errorCode ) 
{ 
   LS_STATUS_CODE   status; 
   char             errorText[200]; 
 
 
   status = LSGetMessage( handle, errorCode, (LS_STR FAR *)errorText, 200); 
   if ( LS_TEXT_UNAVAILABLE == status ) 
      printf("Error: No message catalog available.\n"); 
   else 
      if ( LS_UNKNOWN_STATUS == status ) 
         printf("Error: Unknown error code was used.\n"); 
      else 
         printf("Error: %s\n", errorText); 
} 
 
 
/* 
 * Use the algorithmic approach to calculate the message digest to send 
 * to the server. 
 */ 
LS_STATUS_CODE getChallenge( 
      LS_CHALLENGE    FAR *challenge, 
      LS_STR          FAR *theString, 
      LS_ULONG        unitsConsumed, 
      LS_ULONG        unitsReserved, 
      LS_STR          FAR *logComment, 
      LS_ULONG        R, 
      LS_ULONG        X, 
      LS_ULONG        typeOfCall) 
{ 
   MD4_CTX        MD; 
   char           secret[4]; 
   LS_STR         hashString[200], FAR *hashStringPtr; 
   int            bufferLen, bytes, i; 
 
 
   /* 
    * Set the protocol, index, random number, and size on the challenge 
    * argument. 
    */ 
   challenge->Protocol = LS_BASIC_PROTOCOL; 
   challenge->ChallengeData.SecretIndex = X; 
   challenge->ChallengeData.Random = R; 
 
   /* 
    * Get the secret.  NOTE: Greater steps should be taken to obsure the 
    * secrets than is shown.  Also, more secrets should be used than 4. 
    * Several methods are shown on how the secret can be specified. 
    */ 
   switch ( X ) 
      { 
      case 1: 
         PlaceInt( secret, 0x7778797A ); 
         break; 
      case 2: 
         PlaceInt( secret, 0x31323334 ); 
         break; 
      case 3: 
         PlaceInt( secret, 1633837924 ); 
         break; 
      case 4: 
         PlaceInt( secret, 926431536 ); 
         break; 
      default: 
         printf("Index out of range\n"); 
         return( 0 ); 
         break; 
      } 
 
   /* 
    * Compute the hash string to be used on input to the MD4 algorithm. 
    */ 
   strcpy( hashString, theString ); 
   hashStringPtr = hashString + strlen( theString ); 
   bufferLen = strlen( theString ); 
 
   /* 
    * Add units consumed only for LSUpdate call 
    */ 
   if ( LS_UPDATE == typeOfCall ) 
      { 
      bytes = PlaceInt( hashStringPtr, (LS_ULONG) unitsConsumed ); 
      hashStringPtr += bytes; 
      bufferLen += bytes; 
      } 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) unitsReserved ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   strcpy( hashStringPtr, logComment ); 
   bufferLen += strlen( logComment ); 
   hashStringPtr += strlen( logComment ); 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) R ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) X ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   for ( i=0; i<4; i++ ) 
      { 
      *hashStringPtr = secret[i]; 
      hashStringPtr++; 
      bufferLen++; 
      } 
 
 
   /* 
    * Make the MD4 calls. 
    */ 
   MD4Init(&MD); 
   MD4Update( 
      (MD4_CTX *) &MD, 
      (unsigned char *) hashString, 
      (unsigned int) bufferLen ); 
   MD4Final( 
      challenge->ChallengeData.MsgDigest.MessageDigest, 
      (MD4_CTX *) &MD); 
 
   challenge->Size = sizeof( challenge->ChallengeData ); 
   return( LS_SUCCESS ); 
} 
 
 
/* 
 * Check the return challenge argument. 
 */ 
int checkChallenge( 
      LS_CHALLENGE    FAR *challenge, 
      LS_STR          FAR *theString, 
      LS_ULONG        unitsConsumed , 
      LS_ULONG        unitsReserved , 
      LS_STR          FAR *logComment , 
      LS_ULONG        unitsGranted , 
      LS_STATUS_CODE  status , 
      LS_ULONG        R, 
      LS_ULONG        X, 
      LS_ULONG        typeOfCall) 
{ 
   char           secret[4]; 
   MD4_CTX        MD; 
   LS_STR         hashString[200]; 
   LS_STR         digest[DIGESTLEN], FAR *hashStringPtr; 
   int            i, bufferLen, bytes, j; 
 
 
   /* 
    * Get the secret.  NOTE: Greater steps should be taken to obsure the 
    * secrets than is shown.  Also, more secrets should be used than 4. 
    * Several methods are shown on how the secret can be specified. 
    */ 
   switch ( X ) 
      { 
      case 1: 
         PlaceInt( secret, 0x7778797A ); 
         break; 
      case 2: 
         PlaceInt( secret, 0x31323334 ); 
         break; 
      case 3: 
         PlaceInt( secret, 1633837924 ); 
         break; 
      case 4: 
         PlaceInt( secret, 926431536 ); 
         break; 
      default: 
         printf("Index out of range\n"); 
         return( 0 ); 
         break; 
      } 
 
   /* 
    * Compute the hash string. 
    */ 
   strcpy( hashString, theString ); 
   hashStringPtr = hashString + strlen( theString ); 
   bufferLen = strlen( theString ); 
 
   /* 
    * Add units consumed only for LSUpdate call 
    */ 
   if ( LS_UPDATE == typeOfCall ) 
      { 
      bytes = PlaceInt( hashStringPtr, (LS_ULONG) unitsConsumed ); 
      hashStringPtr += bytes; 
      bufferLen += bytes; 
      } 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) unitsReserved ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   strcpy( hashStringPtr, logComment ); 
   bufferLen += strlen( logComment ); 
   hashStringPtr += strlen( logComment ); 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) unitsGranted ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) status ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) R ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   bytes = PlaceInt( hashStringPtr, (LS_ULONG) X ); 
   hashStringPtr += bytes; 
   bufferLen += bytes; 
 
   for ( i=0; i<4; i++ ) 
      { 
      *hashStringPtr = secret[i]; 
      hashStringPtr++; 
      bufferLen++; 
      } 
 
 
   /* 
    * Compute the message digest. 
    */ 
   MD4Init(&MD); 
   MD4Update( 
      (MD4_CTX *) &MD, 
      (unsigned char *) hashString, 
      (unsigned int) bufferLen ); 
   MD4Final( 
      digest, 
      (MD4_CTX *) &MD); 
 
   /* 
    * Compare the digest with the one on the challenge 
    */ 
   j = 0 ; 
   for( i = 0; i < DIGESTLEN; i++ ) 
    { 
      if( digest[i] != challenge->ChallengeData.MsgDigest.MessageDigest[i] ) 
         break ; 
      j++ ; 
    } 
 
   if( j == DIGESTLEN ) 
      return( 1 ); 
 
   return( 0 ); 
} 
 
/* 
 * swap bytes 
 */ 
void swap( LS_STR FAR *byte1, LS_STR FAR *byte2 ) 
{ 
   LS_STR           tmp; 
 
   tmp = *byte2; 
   *byte2 = *byte1; 
   *byte1 = tmp; 
} 
 
 
 
/* 
 * Places integer in byte buffer, swapping bytes if necessary 
 */ 
int PlaceInt( char FAR *buffer, LS_ULONG value ) 
{ 
   if ( littleEndian ) 
      { 
      swap(&(((char FAR *) &value)[0]), &(((char FAR *) &value)[3])); 
      swap(&(((char FAR *) &value)[1]), &(((char FAR *) &value)[2])); 
      } 
 
   memcpy( buffer, &value, sizeof( LS_ULONG ) ); 
 
   return( (int) sizeof( LS_ULONG ) ); 
}