windows-nt/Source/XPSP1/NT/ds/security/protocols/digest/nonce.cxx

643 lines
18 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: nonce.cxx
//
// Contents: Context APIs for the Digest security package
// Main entry points into this dll:
// NonceCreate
// NonceValidate
// NonceInitialize
//
// History: ChandanS 26-Jul-1996 Stolen from kerberos\client2\ctxtapi.cxx
// KDamour 16Mar00 Stolen from NTLM ctxtapi.cxx
//
//------------------------------------------------------------------------
#include <global.h>
#include <time.h>
// Hold the Hex representation plus the NULL
char g_cNoncePrivateKey[(2*NONCE_PRIVATE_KEY_BYTESIZE) + 1];
//
// Globals
//
HCRYPTPROV g_hCryptProv = 0; // Handle for CryptoAPI
WORD g_SupportedCrypto = 0; // Supported Crypt Functions bitmask (i.e. SUPPORT_DES)
// Needed for Digest Calculation and Nonce Hash
char *pbSeparator = COLONSTR;
//+--------------------------------------------------------------------
//
// Function: NonceInitialize
//
// Synopsis: This function is to be called
//
// Arguments: None
//
// Returns:
//
// Notes:
// CryptReleaseContext( g_hCryptProv, 0 ) to release the cypt context
//
//---------------------------------------------------------------------
NTSTATUS NTAPI
NonceInitialize(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
BYTE abTemp[NONCE_PRIVATE_KEY_BYTESIZE];
HCRYPTKEY hKey = 0;
DebugLog((DEB_TRACE_FUNC, "NonceInitialize: Entering\n"));
if (g_hCryptProv)
{ // Catch cases where LSA and Usermode running in same addr space
DebugLog((DEB_TRACE, "NonceInitialize: Already Inited Leaving\n"));
return STATUS_SUCCESS;
}
//
// Get a handle to the CSP we'll use for all our hash functions etc
//
if ( !CryptAcquireContext( &g_hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT ) )
{
DebugLog((DEB_ERROR, "NonceInitialize:CryptCreateHash() failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
return(Status);
}
//
// Generate and copy over the random bytes
//
if ( !CryptGenRandom( g_hCryptProv,
NONCE_PRIVATE_KEY_BYTESIZE,
abTemp ) )
{
DebugLog((DEB_ERROR, "NonceInitialize:NonceInitialize CryptGenRandom() failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
return (Status);
}
BinToHex((LPBYTE) abTemp, NONCE_PRIVATE_KEY_BYTESIZE, (LPSTR) g_cNoncePrivateKey);
SetSupportedCrypto();
DebugLog((DEB_TRACE_FUNC, "NonceInitialize:Leaving NonceInitialize\n"));
return (Status);
}
//+--------------------------------------------------------------------
//
// Function: SetSupportedCrypto
//
// Synopsis: Set the bitmask for the supported crypto CSP installed
//
// Arguments: none
//
// Returns: STATUS_DATA_ERROR - error in reading CSP capabilities
// STATUS_SUCCESS - operation completed normally
//
//
//---------------------------------------------------------------------
NTSTATUS NTAPI
SetSupportedCrypto(VOID)
{
NTSTATUS Status = STATUS_SUCCESS;
g_SupportedCrypto = SUPPORT_3DES | SUPPORT_DES | SUPPORT_RC4_40 | SUPPORT_RC4 | SUPPORT_RC4_56;
// FIXFIX use CryptGetProvParam to set to actual installed CSP
return(Status);
}
//+--------------------------------------------------------------------
//
// Function: NonceCreate
//
// Synopsis: This function is to be called once during User Mode initialization
//
// Arguments: pczNonce - pointer to a STRING to fillin
// with a new nonce
//
// Returns: STATUS_DATA_ERROR - input NONCE not enough space
// STATUS_SUCCESS - operation completed normally
//
// Notes: Function will return error if Nonce UNICODE_STRING is not empty
// NONCE FORMAT
// rand-data = rand[16]
// nonce_binary = time-stamp rand-data H(time-stamp ":" rand-data ":" nonce_private_key)
// nonce = hex(nonce_binary)
//
//---------------------------------------------------------------------
NTSTATUS NTAPI
NonceCreate(
IN OUT PSTRING pstrNonce
)
{
NTSTATUS Status = STATUS_SUCCESS;
BYTE abRandomData[RANDDATA_BYTESIZE];
char acRandomHex[(2*RANDDATA_BYTESIZE) + 1];
int cbNonce = 0;
time_t tcurrent = time(NULL);
DebugLog((DEB_TRACE_FUNC, "NonceCreate: Entering\n"));
// Check to make sure that there is enough space on ouput string
// Need room for the Nonce and the NULL terminator
if (!pstrNonce->Buffer)
{
Status = StringAllocate(pstrNonce, NONCE_SIZE);
if (!NT_SUCCESS (Status))
{
Status = SEC_E_INSUFFICIENT_MEMORY;
DebugLog((DEB_ERROR, "NonceCreate: StringAllocate error 0x%x\n", Status));
goto CleanUp;
}
}
if (pstrNonce->MaximumLength < (NONCE_SIZE + 1))
{
DebugLog((DEB_ERROR, "NonceCreate: Input STRING too small\n"));
Status = STATUS_BUFFER_TOO_SMALL;
goto CleanUp;
}
// Copy over the current time
BinToHex((LPBYTE)&tcurrent, sizeof(time_t), (LPSTR) pstrNonce->Buffer);
cbNonce += (sizeof(time_t) * 2);
//
// Generate and copy over the random bytes
//
if ( !CryptGenRandom( g_hCryptProv,
RANDDATA_BYTESIZE,
abRandomData ) )
{
DebugLog((DEB_TRACE, "NonceCreate: CryptGenRandom() failed : 0x%x\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
return (Status);
}
//
// Convert to ASCII, doubling the length, and add to nonce
//
BinToHex( abRandomData, RANDDATA_BYTESIZE, acRandomHex);
memcpy(pstrNonce->Buffer + NONCE_RANDDATA_LOC, acRandomHex, (2 * NONCE_PRIVATE_KEY_BYTESIZE));
//
// Now calculate the Hash. It will be NULL terminated but STRING length does not include NULL
//
Status = NonceHash((LPBYTE) pstrNonce->Buffer, (2 * sizeof(time_t)),
(LPBYTE) acRandomHex, (2 * RANDDATA_BYTESIZE),
(LPBYTE) g_cNoncePrivateKey, (2 * NONCE_PRIVATE_KEY_BYTESIZE),
(LPBYTE) (pstrNonce->Buffer + NONCE_HASH_LOC));
if (!NT_SUCCESS (Status))
{
DebugLog((DEB_ERROR, "NonceCreate: failed %d\n", Status));
goto CleanUp;
}
pstrNonce->Length = NONCE_SIZE;
CleanUp:
if (!NT_SUCCESS(Status))
{
pstrNonce->Length = 0;
}
DebugLog((DEB_TRACE_FUNC, "NonceCreate: Leaving\n"));
return (Status);
}
//+--------------------------------------------------------------------
//
// Function: NonceIsValid
//
// Synopsis: Called with a pointer to a Nonce and returns NTSTATUS This is the
// main function that checks for a valid nonce.
//
// Arguments: None
//
// Returns: NTSTATUS STATUS_SUCCESS if NONCE generated locally and is valid
//
// Notes:
//
//---------------------------------------------------------------------
NTSTATUS
NonceIsValid(
PSTRING pstrNonce
)
{
NTSTATUS Status = STATUS_SUCCESS;
DebugLog((DEB_TRACE_FUNC, "NonceIsValid: Entering\n"));
// Check the size first
if (pstrNonce->Length != NONCE_SIZE)
{
DebugLog((DEB_ERROR, "NonceIsValid: Incorrect size for the Nonce\n"));
return STATUS_UNSUCCESSFUL;
}
if (!pstrNonce->Buffer)
{
DebugLog((DEB_ERROR, "NonceIsValid: NULL pointer for the Nonce\n"));
return STATUS_UNSUCCESSFUL;
}
if (NonceIsTampered(pstrNonce))
{
DebugLog((DEB_ERROR, "NonceIsValid: Nonce hash does not match\n"));
return STATUS_UNSUCCESSFUL;
}
DebugLog((DEB_TRACE_FUNC, "NonceIsValid: Leaving\n"));
return (Status);
}
/*++
Routine Description:
Creates MD5 hash of input buffer
Arguments:
pbData - data to hash
cbData - size of data pointed to by pbData
pbHash - buffer that receives hash; is assumed to be big enough to contain MD5 hash
Return Value:
TRUE if successful, FALSE if not
--*/
BOOL HashData( BYTE *pbData,
DWORD cbData,
BYTE *pbHash )
{
HCRYPTHASH hHash = NULL;
DebugLog((DEB_TRACE_FUNC, "HashData: Entering\n"));
if ( !CryptCreateHash( g_hCryptProv,
CALG_MD5,
0,
0,
&hHash ) )
{
DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError()));
return FALSE;
}
if ( !CryptHashData( hHash,
pbData,
cbData,
0 ) )
{
DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError()));
CryptDestroyHash( hHash );
return FALSE;
}
DWORD cbHash = MD5_HASH_BYTESIZE;
if ( !CryptGetHashParam( hHash,
HP_HASHVAL,
pbHash,
&cbHash,
0 ) )
{
DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError()));
CryptDestroyHash( hHash );
return FALSE;
}
CryptDestroyHash( hHash );
DebugLog((DEB_TRACE_FUNC, "HashData: Leaving\n"));
return TRUE;
}
/*++
Routine Description:
Creates MD5 hash of the Nonce values
Arguments:
pbTime - pointer to char buffer encoded Time()
cbTime - number of bytes in encoded Time() buffer to process
pbRandom - pointer to char buffer encoded random sequence
cbRandom - number of bytes in encoded random buffer to process
pbTKey - pointer to char buffer encoded private key
cbKey - number of bytes in encoded private key buffer to process
pbHash - pointer to char buffer encoded Nonce Hash
cbHash - number of bytes in encoded Time() buffer to process
Return Value:
STATUS_SUCCESS - normal completion
--*/
NTSTATUS NTAPI
NonceHash( IN LPBYTE pbTime,
IN DWORD cbTime,
IN LPBYTE pbRandom,
IN DWORD cbRandom,
IN LPBYTE pbKey,
IN DWORD cbKey,
OUT LPBYTE pbHash)
{
NTSTATUS Status = STATUS_SUCCESS;
HCRYPTHASH hHash = NULL;
DWORD cbHash = MD5_HASH_BYTESIZE; // Number of bytes for MD5 hash
unsigned char abHashBin[MD5_HASH_BYTESIZE];
DebugLog((DEB_TRACE, "NonceHash: Entering\n"));
if ( !CryptCreateHash( g_hCryptProv,
CALG_MD5,
0,
0,
&hHash ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptHashData( hHash,
pbTime,
cbTime,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptHashData( hHash,
(const unsigned char *)pbSeparator,
COLONSTR_LEN,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptHashData( hHash,
pbRandom,
cbRandom,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptHashData( hHash,
(const unsigned char *)pbSeparator,
COLONSTR_LEN,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptHashData( hHash,
pbKey,
cbKey,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
if ( !CryptGetHashParam( hHash,
HP_HASHVAL,
abHashBin,
&cbHash,
0 ) )
{
DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
goto CleanUp;
}
// Now convert the Hash to hex
BinToHex(abHashBin, MD5_HASH_BYTESIZE, (char *)pbHash);
CleanUp:
if (hHash)
{
CryptDestroyHash( hHash );
hHash = NULL;
}
DebugLog((DEB_TRACE_FUNC, "NonceHash: Leaving\n"));
return(Status);
}
//+--------------------------------------------------------------------
//
// Function: NonceIsExpired
//
// Synopsis: Check the timestamp to make sure that Nonce is still valid
//
// Arguments: None
//
// Returns: TRUE/FALSE if Nonce is expired
//
// Notes: Called from NonceIsValid
//
//---------------------------------------------------------------------
BOOL NonceIsExpired(PSTRING pstrNonce)
{
DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: Entering\n"));
time_t tcurrent = time(NULL);
time_t tnonce = 0;
// time-stamp is the first bytes in the nonce
HexToBin(pstrNonce->Buffer, (2*TIMESTAMP_BYTESIZE), (unsigned char *)&tnonce);
// If LifeTime set to zero - nonces never expire
if (((unsigned long)tnonce > (unsigned long)tcurrent) ||
(g_dwParameter_Lifetime && (((unsigned long)tcurrent - (unsigned long)tnonce) > g_dwParameter_Lifetime)))
{
DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: NonceHash has expired. Expired = TRUE\n"));
return TRUE;
}
DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: Leaving Expired = FALSE\n"));
return FALSE;
}
//+--------------------------------------------------------------------
//
// Function: NonceIsTampered
//
// Synopsis: Check the hash matches for the Nonce
//
// Arguments: None
//
// Returns: TRUE/FALSE if Nonce hash fails check
//
// Notes: Called from NonceIsValid
//
//---------------------------------------------------------------------
BOOL NonceIsTampered(PSTRING pstrNonce)
{
BOOL bStatus = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
unsigned char abHashHex[(2*MD5_HASH_BYTESIZE) + 1];
DebugLog((DEB_TRACE_FUNC, "NonceIsTampered:Entering \n"));
Status = NonceHash((LPBYTE) (pstrNonce->Buffer + NONCE_TIME_LOC), (2 * sizeof(time_t)),
(LPBYTE) (pstrNonce->Buffer + NONCE_RANDDATA_LOC), (2 * RANDDATA_BYTESIZE),
(LPBYTE) g_cNoncePrivateKey, (2 * NONCE_PRIVATE_KEY_BYTESIZE),
(LPBYTE) abHashHex);
if (!NT_SUCCESS (Status))
{
DebugLog((DEB_ERROR, "NonceIsTampered: NonceHash has failed %d\n", Status));
bStatus = TRUE;
goto CleanUp;
}
if (memcmp(abHashHex, (pstrNonce->Buffer + NONCE_HASH_LOC), (2 * MD5_HASH_BYTESIZE)))
{
DebugLog((DEB_ERROR, "NonceIsTampered: memcmp failed\n"));
bStatus = TRUE;
goto CleanUp;
}
CleanUp:
DebugLog((DEB_TRACE_FUNC, "NonceIsTampered: Leaving\n"));
return bStatus;
}
//+--------------------------------------------------------------------
//
// Function: OpaqueCreate
//
// Synopsis: Creates an Opaque string composed of OPAQUE_SIZE of random data
//
// Arguments: pstrOpque - pointer to a STRING to fillin
// with a new opaque
//
// Returns: STATUS_DATA_ERROR - input NONCE not enough space
// STATUS_SUCCESS - operation completed normally
//
// Notes: Function will return error if Nonce STRING is not empty
// OPAQUE FORMAT
// opaque_binary = rand[OPAQUE_SIZE]
// nonce = Hex(opaque_binary)
//
//---------------------------------------------------------------------
NTSTATUS NTAPI
OpaqueCreate(
IN OUT PSTRING pstrOpaque
)
{
NTSTATUS Status = STATUS_SUCCESS;
BYTE abRandomData[OPAQUE_RANDATA_SIZE];
char acRandomHex[(2*OPAQUE_RANDATA_SIZE) + 1];
int cbNonce = 0;
DebugLog((DEB_TRACE_FUNC, "OpaqueCreate: Entering\n"));
// Check to make sure that there is enough space on ouput string
// Need room for the Nonce and the NULL terminator
if (!pstrOpaque->Buffer)
{
Status = StringAllocate(pstrOpaque, OPAQUE_SIZE);
if (!NT_SUCCESS (Status))
{
Status = SEC_E_INSUFFICIENT_MEMORY;
DebugLog((DEB_ERROR, "OpaqueCreate: StringAllocate error 0x%x\n", Status));
goto CleanUp;
}
}
else if (pstrOpaque->MaximumLength < ((2 * OPAQUE_RANDATA_SIZE) + 1))
{
DebugLog((DEB_ERROR, "OpaqueCreate: Input STRING too small\n"));
Status = STATUS_BUFFER_TOO_SMALL;
goto CleanUp;
}
//
// Generate and copy over the random bytes
//
if ( !CryptGenRandom( g_hCryptProv,
OPAQUE_RANDATA_SIZE,
abRandomData ) )
{
DebugLog((DEB_TRACE, "OpaqueCreate: CryptGenRandom() failed : 0x%lx\n", GetLastError()));
Status = STATUS_INTERNAL_ERROR;
return (Status);
}
//
// Convert to ASCII, doubling the length, and add to nonce
//
BinToHex( abRandomData, OPAQUE_RANDATA_SIZE, pstrOpaque->Buffer);
pstrOpaque->Length = (2 * OPAQUE_RANDATA_SIZE);
CleanUp:
if (!NT_SUCCESS(Status))
{
pstrOpaque->Length = 0;
}
DebugLog((DEB_TRACE_FUNC, "OpaqueCreate: Leaving\n"));
return (Status);
}