Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

763 lines
18 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
data.c
Abstract:
Arbitrary length data encryption functions implementation :
RtlEncryptData
RtlDecryptData
Author:
David Chalmers (Davidc) 12-16-91
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <crypt.h>
#include <engine.h>
//
// Version number of encrypted data
// Update this number if the method used encrypt the data changes
//
#define DATA_ENCRYPTION_VERSION 1
//
// Private data types
//
typedef struct _CRYPTP_BUFFER {
ULONG Length; // Number of valid bytes in buffer
ULONG MaximumLength; // Number of bytes pointed to by buffer
PCHAR Buffer;
PCHAR Pointer; // Points into buffer
} CRYPTP_BUFFER;
typedef CRYPTP_BUFFER *PCRYPTP_BUFFER;
//
// Internal helper macros
#define AdvanceCypherData(p) ((PCYPHER_BLOCK)(((PCRYPTP_BUFFER)p)->Pointer)) ++
#define AdvanceClearData(p) ((PCLEAR_BLOCK)(((PCRYPTP_BUFFER)p)->Pointer)) ++
//
// Private routines
//
VOID
InitializeBuffer(
OUT PCRYPTP_BUFFER PrivateBuffer,
IN PCRYPT_BUFFER PublicBuffer
)
/*++
Routine Description:
Internal helper routine
Copies fields from public buffer into private buffer.
Sets the Pointer field of the private buffer to the
base of the buffer.
Arguments:
PrivateBuffer - out internal buffer we want to represent the public structure.
PublicBuffer - the buffer the caller passed us
Return Values:
None
--*/
{
PrivateBuffer->Length = PublicBuffer->Length;
PrivateBuffer->MaximumLength = PublicBuffer->MaximumLength;
PrivateBuffer->Buffer = PublicBuffer->Buffer;
PrivateBuffer->Pointer = PublicBuffer->Buffer;
}
BOOLEAN
ValidateDataKey(
IN PCRYPTP_BUFFER DataKey,
IN PBLOCK_KEY BlockKey
)
/*++
Routine Description:
Internal helper routine
Checks the validity of the data key and constructs a minimum length
key in the passed blockkey if the datakey is not long enough.
Arguments:
DataKey - The data key
Return Values:
TRUE if the key is valid, otherwise FALSE
--*/
{
if ( ( DataKey->Length == 0 ) ||
( DataKey->Buffer == NULL ) ) {
return(FALSE);
}
if (DataKey->Length < BLOCK_KEY_LENGTH) {
// Make up a minimum length key from the small data key we were
// given. Store it in the passed blockkey variable and point
// the datakey buffer at this temporary storage.
ULONG DataIndex, BlockIndex;
DataIndex = 0;
for (BlockIndex = 0; BlockIndex < BLOCK_KEY_LENGTH; BlockIndex ++) {
((PCHAR)BlockKey)[BlockIndex] = DataKey->Buffer[DataIndex];
DataIndex ++;
if (DataIndex >= DataKey->Length) {
DataIndex = 0;
}
}
// Point the buffer at our constructed block key
DataKey->Buffer = (PCHAR)BlockKey;
DataKey->Pointer = (PCHAR)BlockKey;
DataKey->Length = BLOCK_KEY_LENGTH;
DataKey->MaximumLength = BLOCK_KEY_LENGTH;
}
return(TRUE);
}
VOID
AdvanceDataKey(
IN PCRYPTP_BUFFER DataKey
)
/*++
Routine Description:
Internal helper routine
Moves the data key pointer on to point at the key to use to encrypt
the next data block. Wraps round at end of key data.
Arguments:
DataKey - The data key
Return Values:
STATUS_SUCCESS - No problems
--*/
{
if (DataKey->Length > BLOCK_KEY_LENGTH) {
PCHAR EndPointer;
// Advance pointer and wrap
DataKey->Pointer += BLOCK_KEY_LENGTH;
EndPointer = DataKey->Pointer + BLOCK_KEY_LENGTH;
if (EndPointer > &(DataKey->Buffer[DataKey->Length])) {
ULONG_PTR Overrun;
Overrun = EndPointer - &(DataKey->Buffer[DataKey->Length]);
DataKey->Pointer = DataKey->Buffer + (BLOCK_KEY_LENGTH - Overrun);
}
}
}
ULONG
CalculateCypherDataLength(
IN PCRYPTP_BUFFER ClearData
)
/*++
Routine Description:
Internal helper routine
Returns the number of bytes required to encrypt the specified number
of clear data bytes.
Arguments:
ClearData - The clear data
Return Values:
Number of cypher bytes required.
--*/
{
ULONG CypherDataLength;
ULONG BlockExcess;
// We always store the length of the clear data as a whole block.
CypherDataLength = CYPHER_BLOCK_LENGTH + ClearData->Length;
// Round up to the next block
BlockExcess = CypherDataLength % CYPHER_BLOCK_LENGTH;
if (BlockExcess > 0) {
CypherDataLength += CYPHER_BLOCK_LENGTH - BlockExcess;
}
return(CypherDataLength);
}
NTSTATUS
EncryptDataLength(
IN PCRYPTP_BUFFER Data,
IN PCRYPTP_BUFFER DataKey,
OUT PCRYPTP_BUFFER CypherData
)
/*++
Routine Description:
Internal helper routine
Encrypts the clear data length and puts the encrypted value in the
cypherdatabuffer. Advances the cypherdata buffer and datakey buffer pointers
Arguments:
Data - The buffer whose length is to be encrypted
DataKey - key to use to encrypt data
CypherData - Place to store encrypted data
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
CLEAR_BLOCK ClearBlock;
// Fill the clear block with the data value and a version number
((ULONG *)&ClearBlock)[0] = Data->Length;
((ULONG *)&ClearBlock)[1] = DATA_ENCRYPTION_VERSION;
Status = RtlEncryptBlock(&ClearBlock,
(PBLOCK_KEY)(DataKey->Pointer),
(PCYPHER_BLOCK)(CypherData->Pointer));
// Advance pointers
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
return(Status);
}
NTSTATUS
EncryptFullBlock(
IN OUT PCRYPTP_BUFFER ClearData,
IN OUT PCRYPTP_BUFFER DataKey,
IN OUT PCRYPTP_BUFFER CypherData
)
/*++
Routine Description:
Internal helper routine
Encrypts a full block of data from ClearData and puts the encrypted
data in CypherData.
Both cleardata, datakey and cypherdata pointers are advanced.
Arguments:
ClearData - Pointer to the cleardata buffer
DataKey - key to use to encrypt data
CypherData - Pointer to cypherdata buffer.
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
Status = RtlEncryptBlock((PCLEAR_BLOCK)(ClearData->Pointer),
(PBLOCK_KEY)(DataKey->Pointer),
(PCYPHER_BLOCK)(CypherData->Pointer));
// Advance pointers
AdvanceClearData(ClearData);
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
return(Status);
}
NTSTATUS
EncryptPartialBlock(
IN OUT PCRYPTP_BUFFER ClearData,
IN OUT PCRYPTP_BUFFER DataKey,
IN OUT PCRYPTP_BUFFER CypherData,
IN ULONG Remaining
)
/*++
Routine Description:
Internal helper routine
Encrypts a partial block of data from ClearData and puts the full
encrypted data block in cypherdata.
Both cleardata, datakey and cypherdata pointers are advanced.
Arguments:
ClearData - Pointer to the cleardata buffer
DataKey - key to use to encrypt data
CypherData - Pointer to cypherdata buffer.
Remaining - the number of bytes remaining in cleardata buffer
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
CLEAR_BLOCK ClearBlockBuffer;
PCLEAR_BLOCK ClearBlock = &ClearBlockBuffer;
ASSERTMSG("EncryptPartialBlock called with a block or more", Remaining < CLEAR_BLOCK_LENGTH);
// Copy the remaining bytes into a clear block buffer
while (Remaining > 0) {
*((PCHAR)ClearBlock) ++ = *(ClearData->Pointer) ++;
Remaining --;
}
// Zero pad
while (ClearBlock < &((&ClearBlockBuffer)[1])) {
*((PCHAR)ClearBlock) ++ = 0;
}
Status = RtlEncryptBlock(&ClearBlockBuffer,
(PBLOCK_KEY)(DataKey->Pointer),
(PCYPHER_BLOCK)(CypherData->Pointer));
// Advance pointers
AdvanceClearData(ClearData);
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
return(Status);
}
NTSTATUS
DecryptDataLength(
IN PCRYPTP_BUFFER CypherData,
IN PCRYPTP_BUFFER DataKey,
OUT PCRYPTP_BUFFER Data
)
/*++
Routine Description:
Internal helper routine
Decrypts the data length pointed to by the cypherdata buffer and puts the
decrypted value in the length field of the data structure.
Advances the cypherdata buffer and datakey buffer pointers
Arguments:
CypherData - The buffer containing the encrypted length
DataKey - key to use to decrypt data
Data - Decrypted length field is stored in the length field of this struct.
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
CLEAR_BLOCK ClearBlock;
ULONG Version;
Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
(PBLOCK_KEY)(DataKey->Pointer),
&ClearBlock);
if (!NT_SUCCESS(Status)) {
return(Status);
}
// Advance pointers
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
// Copy the decrypted length into the data structure.
Data->Length = ((ULONG *)&ClearBlock)[0];
// Check the version
Version = ((ULONG *)&ClearBlock)[1];
if (Version != DATA_ENCRYPTION_VERSION) {
return(STATUS_UNKNOWN_REVISION);
}
return(STATUS_SUCCESS);
}
NTSTATUS
DecryptFullBlock(
IN OUT PCRYPTP_BUFFER CypherData,
IN OUT PCRYPTP_BUFFER DataKey,
IN OUT PCRYPTP_BUFFER ClearData
)
/*++
Routine Description:
Internal helper routine
Decrypts a full block of data from CypherData and puts the encrypted
data in ClearData.
Both cleardata, datakey and cypherdata pointers are advanced.
Arguments:
CypherData - Pointer to cypherdata buffer.
ClearData - Pointer to the cleardata buffer
DataKey - key to use to encrypt data
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
(PBLOCK_KEY)(DataKey->Pointer),
(PCLEAR_BLOCK)(ClearData->Pointer));
// Advance pointers
AdvanceClearData(ClearData);
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
return(Status);
}
NTSTATUS
DecryptPartialBlock(
IN OUT PCRYPTP_BUFFER CypherData,
IN OUT PCRYPTP_BUFFER DataKey,
IN OUT PCRYPTP_BUFFER ClearData,
IN ULONG Remaining
)
/*++
Routine Description:
Internal helper routine
Decrypts a full block of data from CypherData and puts the partial
decrypted data block in cleardata.
Both cleardata, datakey and cypherdata pointers are advanced.
Arguments:
CypherData - Pointer to cypherdata buffer.
ClearData - Pointer to the cleardata buffer
DataKey - key to use to encrypt data
Remaining - the number of bytes remaining in cleardata buffer
Return Values:
STATUS_SUCCESS - Success.
STATUS_UNSUCCESSFUL - Something failed.
--*/
{
NTSTATUS Status;
CLEAR_BLOCK ClearBlockBuffer;
PCLEAR_BLOCK ClearBlock = &ClearBlockBuffer;
ASSERTMSG("DecryptPartialBlock called with a block or more", Remaining < CLEAR_BLOCK_LENGTH);
// Decrypt the block into a local clear block
Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
(PBLOCK_KEY)(DataKey->Pointer),
&ClearBlockBuffer);
if (!NT_SUCCESS(Status)) {
return(Status);
}
// Copy the decrypted bytes into the cleardata buffer.
while (Remaining > 0) {
*(ClearData->Pointer) ++ = *((PCHAR)ClearBlock) ++;
Remaining --;
}
// Advance pointers
AdvanceClearData(ClearData);
AdvanceCypherData(CypherData);
AdvanceDataKey(DataKey);
return(Status);
}
//
// Public functions
//
NTSTATUS
RtlEncryptData(
IN PCLEAR_DATA ClearData,
IN PDATA_KEY DataKey,
OUT PCYPHER_DATA CypherData
)
/*++
Routine Description:
Takes an arbitrary length block of data and encrypts it with a
data key producing an encrypted block of data.
Arguments:
ClearData - The data to be encrypted.
DataKey - The key to use to encrypt the data
CypherData - Encrypted data is returned here
Return Values:
STATUS_SUCCESS - The data was encrypted successfully. The encrypted
data is in CypherData. The length of the encrypted
data is is CypherData->Length.
STATUS_BUFFER_TOO_SMALL - CypherData.MaximumLength is too small to
contain the encrypted data.
CypherData->Length contains the number of bytes required.
STATUS_INVALID_PARAMETER_2 - Block key is invalid
STATUS_UNSUCCESSFUL - Something failed.
The CypherData is undefined.
--*/
{
NTSTATUS Status;
ULONG CypherDataLength;
ULONG Remaining = ClearData->Length;
CRYPTP_BUFFER CypherDataBuffer;
CRYPTP_BUFFER ClearDataBuffer;
CRYPTP_BUFFER DataKeyBuffer;
BLOCK_KEY BlockKey; // Only used if datakey less than a block long
InitializeBuffer(&ClearDataBuffer, (PCRYPT_BUFFER)ClearData);
InitializeBuffer(&CypherDataBuffer, (PCRYPT_BUFFER)CypherData);
InitializeBuffer(&DataKeyBuffer, (PCRYPT_BUFFER)DataKey);
// Check the key is OK
if (!ValidateDataKey(&DataKeyBuffer, &BlockKey)) {
return(STATUS_INVALID_PARAMETER_2);
}
// Find out how big we need the cypherdata buffer to be
CypherDataLength = CalculateCypherDataLength(&ClearDataBuffer);
// Fail if cypher data buffer too small
if (CypherData->MaximumLength < CypherDataLength) {
CypherData->Length = CypherDataLength;
return(STATUS_BUFFER_TOO_SMALL);
}
//
// Encrypt the clear data length into the start of the cypher data.
//
Status = EncryptDataLength(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer);
if (!NT_SUCCESS(Status)) {
return(Status);
}
//
// Encrypt the clear data a block at a time.
//
while (Remaining >= CLEAR_BLOCK_LENGTH) {
Status = EncryptFullBlock(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer);
if (!NT_SUCCESS(Status)) {
return(Status);
}
Remaining -= CLEAR_BLOCK_LENGTH;
}
//
// Encrypt any partial block that remains
//
if (Remaining > 0) {
Status = EncryptPartialBlock(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer, Remaining);
if (!NT_SUCCESS(Status)) {
return(Status);
}
}
// Return the encrypted data length
CypherData->Length = CypherDataLength;
return(STATUS_SUCCESS);
}
NTSTATUS
RtlDecryptData(
IN PCYPHER_DATA CypherData,
IN PDATA_KEY DataKey,
OUT PCLEAR_DATA ClearData
)
/*++
Routine Description:
Takes an arbitrary block of encrypted data and decrypts it with a
key producing the original clear block of data.
Arguments:
CypherData - The data to be decrypted
DataKey - The key to use to decrypt data
ClearData - The decrpted data of data is returned here
Return Values:
STATUS_SUCCESS - The data was decrypted successfully. The decrypted
data is in ClearData.
STATUS_BUFFER_TOO_SMALL - ClearData->MaximumLength is too small to
contain the decrypted data.
ClearData->Length contains the number of bytes required.
STATUS_INVALID_PARAMETER_2 - Block key is invalid
STATUS_UNSUCCESSFUL - Something failed.
The ClearData is undefined.
--*/
{
NTSTATUS Status;
ULONG Remaining;
CRYPTP_BUFFER CypherDataBuffer;
CRYPTP_BUFFER ClearDataBuffer;
CRYPTP_BUFFER DataKeyBuffer;
BLOCK_KEY BlockKey; // Only used if datakey less than a block long
InitializeBuffer(&ClearDataBuffer, (PCRYPT_BUFFER)ClearData);
InitializeBuffer(&CypherDataBuffer, (PCRYPT_BUFFER)CypherData);
InitializeBuffer(&DataKeyBuffer, (PCRYPT_BUFFER)DataKey);
// Check the key is OK
if (!ValidateDataKey(&DataKeyBuffer, &BlockKey)) {
return(STATUS_INVALID_PARAMETER_2);
}
//
// Decrypt the clear data length from the start of the cypher data.
//
Status = DecryptDataLength(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer);
if (!NT_SUCCESS(Status)) {
return(Status);
}
// Fail if clear data buffer too small
if (ClearData->MaximumLength < ClearDataBuffer.Length) {
ClearData->Length = ClearDataBuffer.Length;
return(STATUS_BUFFER_TOO_SMALL);
}
//
// Decrypt the clear data a block at a time.
//
Remaining = ClearDataBuffer.Length;
while (Remaining >= CLEAR_BLOCK_LENGTH) {
Status = DecryptFullBlock(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer);
if (!NT_SUCCESS(Status)) {
return(Status);
}
Remaining -= CLEAR_BLOCK_LENGTH;
}
//
// Decrypt any partial block that remains
//
if (Remaining > 0) {
Status = DecryptPartialBlock(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer, Remaining);
if (!NT_SUCCESS(Status)) {
return(Status);
}
}
// Return the length of the decrypted data
ClearData->Length = ClearDataBuffer.Length;
return(STATUS_SUCCESS);
}