/*++ Copyright (c) 1987-1998 Microsoft Corporation Module Name: credderi.c Abstract: Interface to credential derivation facility. Author: Scott Field (sfield) 14-Jan-1998 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: --*/ #include "msp.h" #include "nlp.h" #include #define HMAC_K_PADSIZE (64) // // Prototype for credential derivation routines. // VOID DeriveWithHMAC_SHA1( IN PBYTE pbKeyMaterial, IN DWORD cbKeyMaterial, IN PBYTE pbData, IN DWORD cbData, IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer ); NTSTATUS MspNtDeriveCredential( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferSize, OUT PNTSTATUS ProtocolStatus ) /*++ Routine Description: This routine is the dispatch routine for LsaCallAuthenticationPackage() with a message type of MsV1_0DeriveCredential. Arguments: The arguments to this routine are identical to those of LsaApCallPackage. Only the special attributes of these parameters as they apply to this routine are mentioned here. Return Value: STATUS_SUCCESS - Indicates the service completed successfully. STATUS_QUOTA_EXCEEDED - This error indicates that the logon could not be completed because the client does not have sufficient quota to allocate the return buffer. --*/ { NTSTATUS Status = STATUS_SUCCESS; PMSV1_0_DERIVECRED_REQUEST DeriveCredRequest; PMSV1_0_DERIVECRED_RESPONSE DeriveCredResponse; CLIENT_BUFFER_DESC ClientBufferDesc; PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL; PBYTE pbOwf; ULONG cbOwf; NlpInitClientBuffer( &ClientBufferDesc, ClientRequest ); *ProtocolStatus = STATUS_SUCCESS; UNREFERENCED_PARAMETER(ClientBufferBase); // // Ensure the specified Submit Buffer is of reasonable size and // relocate all of the pointers to be relative to the LSA allocated // buffer. // if ( SubmitBufferSize < sizeof(MSV1_0_DERIVECRED_REQUEST) ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } DeriveCredRequest = (PMSV1_0_DERIVECRED_REQUEST) ProtocolSubmitBuffer; // // validate supported derive types. // if( DeriveCredRequest->DeriveCredType != MSV1_0_DERIVECRED_TYPE_SHA1 && DeriveCredRequest->DeriveCredType != MSV1_0_DERIVECRED_TYPE_SHA1_V2 ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // caller must pass in mixing bits into submit buffer. // if( DeriveCredRequest->DeriveCredInfoLength == 0 ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Make sure the buffer fits in the supplied size // if ( (DeriveCredRequest->DeriveCredInfoLength + sizeof(MSV1_0_DERIVECRED_REQUEST)) > SubmitBufferSize ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Get the OWF password for this session. // Status = NlpGetPrimaryCredential( &DeriveCredRequest->LogonId, &Credential, NULL ); if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } // // Allocate a buffer to return to the caller. // *ReturnBufferSize = sizeof(MSV1_0_DERIVECRED_RESPONSE) + A_SHA_DIGEST_LEN; Status = NlpAllocateClientBuffer( &ClientBufferDesc, *ReturnBufferSize, *ReturnBufferSize ); if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } ZeroMemory( ClientBufferDesc.MsvBuffer, *ReturnBufferSize ); DeriveCredResponse = (PMSV1_0_DERIVECRED_RESPONSE) ClientBufferDesc.MsvBuffer; // // Fill in the return buffer. // DeriveCredResponse->MessageType = MsV1_0DeriveCredential; DeriveCredResponse->DeriveCredInfoLength = A_SHA_DIGEST_LEN; pbOwf = NULL; cbOwf = 0; if( DeriveCredRequest->DeriveCredType == MSV1_0_DERIVECRED_TYPE_SHA1_V2 ) { // // explicitly requested derivation based on ShaOwfPassword. // if( Credential->ShaPasswordPresent ) { pbOwf = (PBYTE) &(Credential->ShaOwfPassword); // key material is SHA OWF cbOwf = sizeof( SHA_OWF_PASSWORD ); } } else if( DeriveCredRequest->DeriveCredType == MSV1_0_DERIVECRED_TYPE_SHA1 ) { // // explicitly requested derivation based on NtOwfPassword. // if( Credential->NtPasswordPresent ) { pbOwf = (PBYTE) &(Credential->NtOwfPassword); // key material is NT OWF cbOwf = sizeof( NT_OWF_PASSWORD ); } } if( pbOwf == NULL ) { Status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // derive credential from HMAC_SHA1 crypto primitive // (the only supported crypto primitive at the moment) // DeriveWithHMAC_SHA1( pbOwf, cbOwf, DeriveCredRequest->DeriveCredSubmitBuffer, DeriveCredRequest->DeriveCredInfoLength, DeriveCredResponse->DeriveCredReturnBuffer ); // // Flush the buffer to the client's address space. // Status = NlpFlushClientBuffer( &ClientBufferDesc, ProtocolReturnBuffer ); Cleanup: if ( Credential != NULL ) { ZeroMemory( Credential, sizeof(*Credential) ); (*Lsa.FreeLsaHeap)( Credential ); } if ( !NT_SUCCESS(Status)) { NlpFreeClientBuffer( &ClientBufferDesc ); } return(Status); } VOID DeriveWithHMAC_SHA1( IN PBYTE pbKeyMaterial, // input key material IN DWORD cbKeyMaterial, IN PBYTE pbData, // input mixing data IN DWORD cbData, IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer ) { unsigned __int64 rgbKipad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ]; unsigned __int64 rgbKopad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ]; A_SHA_CTX sSHAHash; DWORD dwBlock; // truncate if( cbKeyMaterial > HMAC_K_PADSIZE ) { cbKeyMaterial = HMAC_K_PADSIZE; } ZeroMemory(rgbKipad, sizeof(rgbKipad)); ZeroMemory(rgbKopad, sizeof(rgbKopad)); CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial); CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial); // Kipad, Kopad are padded sMacKey. Now XOR across... for( dwBlock = 0; dwBlock < (HMAC_K_PADSIZE/sizeof(unsigned __int64)) ; dwBlock++ ) { rgbKipad[dwBlock] ^= 0x3636363636363636; rgbKopad[dwBlock] ^= 0x5C5C5C5C5C5C5C5C; } // prepend Kipad to data, Hash to get H1 A_SHAInit(&sSHAHash); A_SHAUpdate(&sSHAHash, (PBYTE)rgbKipad, sizeof(rgbKipad)); A_SHAUpdate(&sSHAHash, pbData, cbData); // Finish off the hash A_SHAFinal(&sSHAHash, rgbHMAC); // prepend Kopad to H1, hash to get HMAC // note: done in place to avoid buffer copies // final hash: output value into passed-in buffer A_SHAInit(&sSHAHash); A_SHAUpdate(&sSHAHash, (PBYTE)rgbKopad, sizeof(rgbKopad)); A_SHAUpdate(&sSHAHash, rgbHMAC, A_SHA_DIGEST_LEN); A_SHAFinal(&sSHAHash, rgbHMAC); RtlSecureZeroMemory( rgbKipad, sizeof(rgbKipad) ); RtlSecureZeroMemory( rgbKopad, sizeof(rgbKopad) ); RtlSecureZeroMemory( &sSHAHash, sizeof(sSHAHash) ); return; }