| /** @file | |
| The Miscellaneous Routines for TlsDxe driver. | |
| Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "TlsImpl.h" | |
| /** | |
| Encrypt the message listed in fragment. | |
| @param[in] TlsInstance The pointer to the TLS instance. | |
| @param[in, out] FragmentTable Pointer to a list of fragment. | |
| On input these fragments contain the TLS header and | |
| plain text TLS payload; | |
| On output these fragments contain the TLS header and | |
| cipher text TLS payload. | |
| @param[in] FragmentCount Number of fragment. | |
| @retval EFI_SUCCESS The operation completed successfully. | |
| @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. | |
| @retval EFI_ABORTED TLS session state is incorrect. | |
| @retval Others Other errors as indicated. | |
| **/ | |
| EFI_STATUS | |
| TlsEncryptPacket ( | |
| IN TLS_INSTANCE *TlsInstance, | |
| IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable, | |
| IN UINT32 *FragmentCount | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINT32 BytesCopied; | |
| UINT32 BufferInSize; | |
| UINT8 *BufferIn; | |
| UINT8 *BufferInPtr; | |
| TLS_RECORD_HEADER *RecordHeaderIn; | |
| UINT16 ThisPlainMessageSize; | |
| TLS_RECORD_HEADER *TempRecordHeader; | |
| UINT16 ThisMessageSize; | |
| UINT32 BufferOutSize; | |
| UINT8 *BufferOut; | |
| UINT32 RecordCount; | |
| INTN Ret; | |
| Status = EFI_SUCCESS; | |
| BytesCopied = 0; | |
| BufferInSize = 0; | |
| BufferIn = NULL; | |
| BufferInPtr = NULL; | |
| RecordHeaderIn = NULL; | |
| TempRecordHeader = NULL; | |
| BufferOutSize = 0; | |
| BufferOut = NULL; | |
| RecordCount = 0; | |
| Ret = 0; | |
| // | |
| // Calculate the size according to the fragment table. | |
| // | |
| for (Index = 0; Index < *FragmentCount; Index++) { | |
| BufferInSize += (*FragmentTable)[Index].FragmentLength; | |
| } | |
| // | |
| // Allocate buffer for processing data. | |
| // | |
| BufferIn = AllocateZeroPool (BufferInSize); | |
| if (BufferIn == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| // | |
| // Copy all TLS plain record header and payload into BufferIn. | |
| // | |
| for (Index = 0; Index < *FragmentCount; Index++) { | |
| CopyMem ( | |
| (BufferIn + BytesCopied), | |
| (*FragmentTable)[Index].FragmentBuffer, | |
| (*FragmentTable)[Index].FragmentLength | |
| ); | |
| BytesCopied += (*FragmentTable)[Index].FragmentLength; | |
| } | |
| // | |
| // Count TLS record number. | |
| // | |
| BufferInPtr = BufferIn; | |
| while ((UINTN)BufferInPtr < (UINTN)BufferIn + BufferInSize) { | |
| RecordHeaderIn = (TLS_RECORD_HEADER *)BufferInPtr; | |
| if ((RecordHeaderIn->ContentType != TlsContentTypeApplicationData) || (RecordHeaderIn->Length > TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH)) { | |
| Status = EFI_INVALID_PARAMETER; | |
| goto ERROR; | |
| } | |
| BufferInPtr += TLS_RECORD_HEADER_LENGTH + RecordHeaderIn->Length; | |
| RecordCount++; | |
| } | |
| // | |
| // Allocate enough buffer to hold TLS Ciphertext. | |
| // | |
| BufferOut = AllocateZeroPool (RecordCount * (TLS_RECORD_HEADER_LENGTH + TLS_CIPHERTEXT_RECORD_MAX_PAYLOAD_LENGTH)); | |
| if (BufferOut == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| // | |
| // Parsing buffer. Received packet may have multiple TLS record messages. | |
| // | |
| BufferInPtr = BufferIn; | |
| TempRecordHeader = (TLS_RECORD_HEADER *)BufferOut; | |
| while ((UINTN)BufferInPtr < (UINTN)BufferIn + BufferInSize) { | |
| RecordHeaderIn = (TLS_RECORD_HEADER *)BufferInPtr; | |
| ThisPlainMessageSize = RecordHeaderIn->Length; | |
| TlsWrite (TlsInstance->TlsConn, (UINT8 *)(RecordHeaderIn + 1), ThisPlainMessageSize); | |
| Ret = TlsCtrlTrafficOut (TlsInstance->TlsConn, (UINT8 *)(TempRecordHeader), TLS_RECORD_HEADER_LENGTH + TLS_CIPHERTEXT_RECORD_MAX_PAYLOAD_LENGTH); | |
| if (Ret > 0) { | |
| ThisMessageSize = (UINT16)Ret; | |
| } else { | |
| // | |
| // No data was successfully encrypted, continue to encrypt other messages. | |
| // | |
| DEBUG ((DEBUG_WARN, "TlsEncryptPacket: No data read from TLS object.\n")); | |
| ThisMessageSize = 0; | |
| } | |
| BufferOutSize += ThisMessageSize; | |
| BufferInPtr += TLS_RECORD_HEADER_LENGTH + ThisPlainMessageSize; | |
| TempRecordHeader = (TLS_RECORD_HEADER *)((UINT8 *)TempRecordHeader + ThisMessageSize); | |
| } | |
| FreePool (BufferIn); | |
| BufferIn = NULL; | |
| // | |
| // The caller will be responsible to handle the original fragment table. | |
| // | |
| *FragmentTable = AllocateZeroPool (sizeof (EFI_TLS_FRAGMENT_DATA)); | |
| if (*FragmentTable == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| (*FragmentTable)[0].FragmentBuffer = BufferOut; | |
| (*FragmentTable)[0].FragmentLength = BufferOutSize; | |
| *FragmentCount = 1; | |
| return Status; | |
| ERROR: | |
| if (BufferIn != NULL) { | |
| FreePool (BufferIn); | |
| BufferIn = NULL; | |
| } | |
| if (BufferOut != NULL) { | |
| FreePool (BufferOut); | |
| BufferOut = NULL; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Decrypt the message listed in fragment. | |
| @param[in] TlsInstance The pointer to the TLS instance. | |
| @param[in, out] FragmentTable Pointer to a list of fragment. | |
| On input these fragments contain the TLS header and | |
| cipher text TLS payload; | |
| On output these fragments contain the TLS header and | |
| plain text TLS payload. | |
| @param[in] FragmentCount Number of fragment. | |
| @retval EFI_SUCCESS The operation completed successfully. | |
| @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. | |
| @retval EFI_ABORTED TLS session state is incorrect. | |
| @retval Others Other errors as indicated. | |
| **/ | |
| EFI_STATUS | |
| TlsDecryptPacket ( | |
| IN TLS_INSTANCE *TlsInstance, | |
| IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable, | |
| IN UINT32 *FragmentCount | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINT32 BytesCopied; | |
| UINT8 *BufferIn; | |
| UINT32 BufferInSize; | |
| UINT8 *BufferInPtr; | |
| TLS_RECORD_HEADER *RecordHeaderIn; | |
| UINT16 ThisCipherMessageSize; | |
| TLS_RECORD_HEADER *TempRecordHeader; | |
| UINT16 ThisPlainMessageSize; | |
| UINT8 *BufferOut; | |
| UINT32 BufferOutSize; | |
| UINT32 RecordCount; | |
| INTN Ret; | |
| Status = EFI_SUCCESS; | |
| BytesCopied = 0; | |
| BufferIn = NULL; | |
| BufferInSize = 0; | |
| BufferInPtr = NULL; | |
| RecordHeaderIn = NULL; | |
| TempRecordHeader = NULL; | |
| BufferOut = NULL; | |
| BufferOutSize = 0; | |
| RecordCount = 0; | |
| Ret = 0; | |
| // | |
| // Calculate the size according to the fragment table. | |
| // | |
| for (Index = 0; Index < *FragmentCount; Index++) { | |
| BufferInSize += (*FragmentTable)[Index].FragmentLength; | |
| } | |
| // | |
| // Allocate buffer for processing data | |
| // | |
| BufferIn = AllocateZeroPool (BufferInSize); | |
| if (BufferIn == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| // | |
| // Copy all TLS plain record header and payload to BufferIn | |
| // | |
| for (Index = 0; Index < *FragmentCount; Index++) { | |
| CopyMem ( | |
| (BufferIn + BytesCopied), | |
| (*FragmentTable)[Index].FragmentBuffer, | |
| (*FragmentTable)[Index].FragmentLength | |
| ); | |
| BytesCopied += (*FragmentTable)[Index].FragmentLength; | |
| } | |
| // | |
| // Count TLS record number. | |
| // | |
| BufferInPtr = BufferIn; | |
| while ((UINTN)BufferInPtr < (UINTN)BufferIn + BufferInSize) { | |
| RecordHeaderIn = (TLS_RECORD_HEADER *)BufferInPtr; | |
| if ((RecordHeaderIn->ContentType != TlsContentTypeApplicationData) || (NTOHS (RecordHeaderIn->Length) > TLS_CIPHERTEXT_RECORD_MAX_PAYLOAD_LENGTH)) { | |
| Status = EFI_INVALID_PARAMETER; | |
| goto ERROR; | |
| } | |
| BufferInPtr += TLS_RECORD_HEADER_LENGTH + NTOHS (RecordHeaderIn->Length); | |
| RecordCount++; | |
| } | |
| // | |
| // Allocate enough buffer to hold TLS Plaintext. | |
| // | |
| BufferOut = AllocateZeroPool (RecordCount * (TLS_RECORD_HEADER_LENGTH + TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH)); | |
| if (BufferOut == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| // | |
| // Parsing buffer. Received packet may have multiple TLS record messages. | |
| // | |
| BufferInPtr = BufferIn; | |
| TempRecordHeader = (TLS_RECORD_HEADER *)BufferOut; | |
| while ((UINTN)BufferInPtr < (UINTN)BufferIn + BufferInSize) { | |
| RecordHeaderIn = (TLS_RECORD_HEADER *)BufferInPtr; | |
| ThisCipherMessageSize = NTOHS (RecordHeaderIn->Length); | |
| Ret = TlsCtrlTrafficIn (TlsInstance->TlsConn, (UINT8 *)(RecordHeaderIn), TLS_RECORD_HEADER_LENGTH + ThisCipherMessageSize); | |
| if (Ret != TLS_RECORD_HEADER_LENGTH + ThisCipherMessageSize) { | |
| TlsInstance->TlsSessionState = EfiTlsSessionError; | |
| Status = EFI_ABORTED; | |
| goto ERROR; | |
| } | |
| Ret = 0; | |
| Ret = TlsRead (TlsInstance->TlsConn, (UINT8 *)(TempRecordHeader + 1), TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH); | |
| if (Ret > 0) { | |
| ThisPlainMessageSize = (UINT16)Ret; | |
| } else { | |
| // | |
| // No data was successfully decrypted, continue to decrypt other messages. | |
| // | |
| DEBUG ((DEBUG_WARN, "TlsDecryptPacket: No data read from TLS object.\n")); | |
| ThisPlainMessageSize = 0; | |
| } | |
| CopyMem (TempRecordHeader, RecordHeaderIn, TLS_RECORD_HEADER_LENGTH); | |
| TempRecordHeader->Length = ThisPlainMessageSize; | |
| BufferOutSize += TLS_RECORD_HEADER_LENGTH + ThisPlainMessageSize; | |
| BufferInPtr += TLS_RECORD_HEADER_LENGTH + ThisCipherMessageSize; | |
| TempRecordHeader = (TLS_RECORD_HEADER *)((UINT8 *)TempRecordHeader + TLS_RECORD_HEADER_LENGTH + ThisPlainMessageSize); | |
| } | |
| FreePool (BufferIn); | |
| BufferIn = NULL; | |
| // | |
| // The caller will be responsible to handle the original fragment table | |
| // | |
| *FragmentTable = AllocateZeroPool (sizeof (EFI_TLS_FRAGMENT_DATA)); | |
| if (*FragmentTable == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ERROR; | |
| } | |
| (*FragmentTable)[0].FragmentBuffer = BufferOut; | |
| (*FragmentTable)[0].FragmentLength = BufferOutSize; | |
| *FragmentCount = 1; | |
| return Status; | |
| ERROR: | |
| if (BufferIn != NULL) { | |
| FreePool (BufferIn); | |
| BufferIn = NULL; | |
| } | |
| if (BufferOut != NULL) { | |
| FreePool (BufferOut); | |
| BufferOut = NULL; | |
| } | |
| return Status; | |
| } |