]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpDxe/HttpsSupport.c
NetworkPkg/HttpDxe: HTTPS support over IPv4 and IPv6
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpsSupport.c
diff --git a/NetworkPkg/HttpDxe/HttpsSupport.c b/NetworkPkg/HttpDxe/HttpsSupport.c
new file mode 100644 (file)
index 0000000..478a9e0
--- /dev/null
@@ -0,0 +1,1693 @@
+/** @file
+  Miscellaneous routines specific to Https for HttpDxe driver.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpDriver.h"
+
+/**
+  Returns the first occurrence of a Null-terminated ASCII sub-string in a Null-terminated
+  ASCII string and ignore case during the search process.
+
+  This function scans the contents of the ASCII string specified by String
+  and returns the first occurrence of SearchString and ignore case during the search process.
+  If SearchString is not found in String, then NULL is returned. If the length of SearchString
+  is zero, then String is returned.
+
+  If String is NULL, then ASSERT().
+  If SearchString is NULL, then ASSERT().
+
+  @param[in]  String          A pointer to a Null-terminated ASCII string.
+  @param[in]  SearchString    A pointer to a Null-terminated ASCII string to search for.
+
+  @retval NULL            If the SearchString does not appear in String.
+  @retval others          If there is a match return the first occurrence of SearchingString.
+                          If the length of SearchString is zero,return String.
+
+**/
+CHAR8 *
+AsciiStrCaseStr (
+  IN      CONST CHAR8               *String,
+  IN      CONST CHAR8               *SearchString
+  )
+{
+  CONST CHAR8 *FirstMatch;
+  CONST CHAR8 *SearchStringTmp;
+
+  CHAR8 Src;
+  CHAR8 Dst;
+
+  //
+  // ASSERT both strings are less long than PcdMaximumAsciiStringLength
+  //
+  ASSERT (AsciiStrSize (String) != 0);
+  ASSERT (AsciiStrSize (SearchString) != 0);
+
+  if (*SearchString == '\0') {
+    return (CHAR8 *) String;
+  }
+
+  while (*String != '\0') {
+    SearchStringTmp = SearchString;
+    FirstMatch = String;
+
+    while ((*SearchStringTmp != '\0')
+            && (*String != '\0')) {
+      Src = *String;
+      Dst = *SearchStringTmp;
+
+      if ((Src >= 'A') && (Src <= 'Z')) {
+        Src -= ('A' - 'a');
+      }
+
+      if ((Dst >= 'A') && (Dst <= 'Z')) {
+        Dst -= ('A' - 'a');
+      }
+
+      if (Src != Dst) {
+        break;
+      }
+
+      String++;
+      SearchStringTmp++;
+    }
+
+    if (*SearchStringTmp == '\0') {
+      return (CHAR8 *) FirstMatch;
+    }
+
+    String = FirstMatch + 1;
+  }
+
+  return NULL;
+}
+
+/**
+  The callback function to free the net buffer list.
+
+  @param[in]  Arg The opaque parameter.
+
+**/
+VOID
+EFIAPI
+FreeNbufList (
+  IN VOID *Arg
+  )
+{
+  ASSERT (Arg != NULL);
+
+  NetbufFreeList ((LIST_ENTRY *) Arg);
+  FreePool (Arg);
+}
+
+/**
+  Check whether the Url is from Https.
+
+  @param[in]    Url             The pointer to a HTTP or HTTPS URL string.
+
+  @retval TRUE                  The Url is from HTTPS.
+  @retval FALSE                 The Url is from HTTP.
+
+**/
+BOOLEAN
+IsHttpsUrl (
+  IN CHAR8    *Url
+  )
+{
+  CHAR8  *Tmp;
+
+  Tmp = NULL;
+
+  Tmp = AsciiStrCaseStr (Url, HTTPS_FLAG);
+  if (Tmp != NULL && Tmp == Url) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+  @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.
+  @param[out] TlsProto              Pointer to the EFI_TLS_PROTOCOL instance.
+  @param[out] TlsConfiguration      Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+
+  @return  The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+**/
+EFI_HANDLE
+EFIAPI
+TlsCreateChild (
+  IN  EFI_HANDLE                     ImageHandle,
+  OUT EFI_TLS_PROTOCOL               **TlsProto,
+  OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_SERVICE_BINDING_PROTOCOL  *TlsSb;
+  EFI_HANDLE                    TlsChildHandle;
+
+  TlsSb          = NULL;
+  TlsChildHandle = 0;
+
+  //
+  // Locate TlsServiceBinding protocol.
+  //
+  gBS->LocateProtocol (
+     &gEfiTlsServiceBindingProtocolGuid,
+     NULL,
+     (VOID **) &TlsSb
+     );
+  if (TlsSb == NULL) {
+    return NULL;
+  }
+
+  Status = TlsSb->CreateChild (TlsSb, &TlsChildHandle);
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  Status = gBS->OpenProtocol (
+                  TlsChildHandle,
+                  &gEfiTlsProtocolGuid,
+                  (VOID **) TlsProto,
+                  ImageHandle,
+                  TlsChildHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    TlsSb->DestroyChild (TlsSb, TlsChildHandle);
+    return NULL;
+  }
+
+  Status = gBS->OpenProtocol (
+                  TlsChildHandle,
+                  &gEfiTlsConfigurationProtocolGuid,
+                  (VOID **) TlsConfiguration,
+                  ImageHandle,
+                  TlsChildHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    TlsSb->DestroyChild (TlsSb, TlsChildHandle);
+    return NULL;
+  }
+
+  return TlsChildHandle;
+}
+
+/**
+  Create event for the TLS receive and transmit tokens which are used to receive and
+  transmit TLS related messages.
+
+  @param[in, out]  HttpInstance       Pointer to HTTP_PROTOCOL structure.
+
+  @retval EFI_SUCCESS            The events are created successfully.
+  @retval others                 Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCreateTxRxEvent (
+  IN OUT HTTP_PROTOCOL      *HttpInstance
+  )
+{
+  EFI_STATUS                Status;
+
+  if (!HttpInstance->LocalAddressIsIPv6) {
+    //
+    // For Tcp4TlsTxToken.
+    //
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    HttpCommonNotify,
+                    &HttpInstance->TlsIsTxDone,
+                    &HttpInstance->Tcp4TlsTxToken.CompletionToken.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ERROR;
+    }
+
+    HttpInstance->Tcp4TlsTxData.Push = TRUE;
+    HttpInstance->Tcp4TlsTxData.Urgent = FALSE;
+    HttpInstance->Tcp4TlsTxData.DataLength = 0;
+    HttpInstance->Tcp4TlsTxData.FragmentCount = 1;
+    HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsTxData.DataLength;
+    HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentBuffer = NULL;
+    HttpInstance->Tcp4TlsTxToken.Packet.TxData = &HttpInstance->Tcp4TlsTxData;
+    HttpInstance->Tcp4TlsTxToken.CompletionToken.Status = EFI_NOT_READY;
+
+    //
+    // For Tcp4TlsRxToken.
+    //
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    HttpCommonNotify,
+                    &HttpInstance->TlsIsRxDone,
+                    &HttpInstance->Tcp4TlsRxToken.CompletionToken.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ERROR;
+    }
+
+    HttpInstance->Tcp4TlsRxData.DataLength                       = 0;
+    HttpInstance->Tcp4TlsRxData.FragmentCount                    = 1;
+    HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentLength  = HttpInstance->Tcp4TlsRxData.DataLength ;
+    HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentBuffer  = NULL;
+    HttpInstance->Tcp4TlsRxToken.Packet.RxData          = &HttpInstance->Tcp4TlsRxData;
+    HttpInstance->Tcp4TlsRxToken.CompletionToken.Status = EFI_NOT_READY;
+  } else {
+    //
+    // For Tcp6TlsTxToken.
+    //
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    HttpCommonNotify,
+                    &HttpInstance->TlsIsTxDone,
+                    &HttpInstance->Tcp6TlsTxToken.CompletionToken.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ERROR;
+    }
+
+    HttpInstance->Tcp6TlsTxData.Push = TRUE;
+    HttpInstance->Tcp6TlsTxData.Urgent = FALSE;
+    HttpInstance->Tcp6TlsTxData.DataLength = 0;
+    HttpInstance->Tcp6TlsTxData.FragmentCount = 1;
+    HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsTxData.DataLength;
+    HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentBuffer = NULL;
+    HttpInstance->Tcp6TlsTxToken.Packet.TxData = &HttpInstance->Tcp6TlsTxData;
+    HttpInstance->Tcp6TlsTxToken.CompletionToken.Status = EFI_NOT_READY;
+
+    //
+    // For Tcp6TlsRxToken.
+    //
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    HttpCommonNotify,
+                    &HttpInstance->TlsIsRxDone,
+                    &HttpInstance->Tcp6TlsRxToken.CompletionToken.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ERROR;
+    }
+
+    HttpInstance->Tcp6TlsRxData.DataLength                       = 0;
+    HttpInstance->Tcp6TlsRxData.FragmentCount                    = 1;
+    HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentLength  = HttpInstance->Tcp6TlsRxData.DataLength ;
+    HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentBuffer  = NULL;
+    HttpInstance->Tcp6TlsRxToken.Packet.RxData          = &HttpInstance->Tcp6TlsRxData;
+    HttpInstance->Tcp6TlsRxToken.CompletionToken.Status = EFI_NOT_READY;
+  }
+
+  return Status;
+
+ERROR:
+  //
+  // Error handling
+  //
+  TlsCloseTxRxEvent (HttpInstance);
+
+  return Status;
+}
+
+/**
+  Close events in the TlsTxToken and TlsRxToken.
+
+  @param[in]  HttpInstance   Pointer to HTTP_PROTOCOL structure.
+
+**/
+VOID
+EFIAPI
+TlsCloseTxRxEvent (
+  IN  HTTP_PROTOCOL        *HttpInstance
+  )
+{
+  ASSERT (HttpInstance != NULL);
+  if (!HttpInstance->LocalAddressIsIPv6) {
+    if (NULL != HttpInstance->Tcp4TlsTxToken.CompletionToken.Event) {
+      gBS->CloseEvent(HttpInstance->Tcp4TlsTxToken.CompletionToken.Event);
+      HttpInstance->Tcp4TlsTxToken.CompletionToken.Event = NULL;
+    }
+
+    if (NULL != HttpInstance->Tcp4TlsRxToken.CompletionToken.Event) {
+      gBS->CloseEvent (HttpInstance->Tcp4TlsRxToken.CompletionToken.Event);
+      HttpInstance->Tcp4TlsRxToken.CompletionToken.Event = NULL;
+    }
+  } else {
+    if (NULL != HttpInstance->Tcp6TlsTxToken.CompletionToken.Event) {
+      gBS->CloseEvent(HttpInstance->Tcp6TlsTxToken.CompletionToken.Event);
+      HttpInstance->Tcp6TlsTxToken.CompletionToken.Event = NULL;
+    }
+
+    if (NULL != HttpInstance->Tcp6TlsRxToken.CompletionToken.Event) {
+      gBS->CloseEvent (HttpInstance->Tcp6TlsRxToken.CompletionToken.Event);
+      HttpInstance->Tcp6TlsRxToken.CompletionToken.Event = NULL;
+    }
+  }
+}
+
+/**
+  Read the TlsCaCertificate variable and configure it.
+
+  @param[in, out]  HttpInstance       The HTTP instance private data.
+
+  @retval EFI_SUCCESS            TlsCaCertificate is configured.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval EFI_NOT_FOUND          Fail to get 'TlsCaCertificate' variable.
+  @retval Others                 Other error as indicated.
+
+**/
+EFI_STATUS
+TlsConfigCertificate (
+  IN OUT HTTP_PROTOCOL      *HttpInstance
+  )
+{
+  EFI_STATUS          Status;
+  UINT8               *CACert;
+  UINTN               CACertSize;
+  UINT32              Index;
+  EFI_SIGNATURE_LIST  *CertList;
+  EFI_SIGNATURE_DATA  *Cert;
+  UINTN               CertCount;
+  UINT32              ItemDataSize;
+
+  CACert     = NULL;
+  CACertSize = 0;
+  
+  //
+  // Try to read the TlsCaCertificate variable.
+  //
+  Status  = gRT->GetVariable (
+                   EFI_TLS_CA_CERTIFICATE_VARIABLE,
+                   &gEfiTlsCaCertificateGuid,
+                   NULL,
+                   &CACertSize,
+                   NULL
+                   );
+
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    //
+    // Allocate buffer and read the config variable.
+    //
+    CACert = AllocatePool (CACertSize);
+    if (CACert == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = gRT->GetVariable (
+                    EFI_TLS_CA_CERTIFICATE_VARIABLE,
+                    &gEfiTlsCaCertificateGuid,
+                    NULL,
+                    &CACertSize,
+                    CACert
+                    );
+    if (EFI_ERROR (Status)) {
+      //
+      // GetVariable still error or the variable is corrupted.
+      // Fall back to the default value.
+      //
+      FreePool (CACert);
+
+      return EFI_NOT_FOUND;
+    }
+  }
+
+  //
+  // Enumerate all data and erasing the target item.
+  //
+  ItemDataSize = (UINT32) CACertSize;
+  CertList = (EFI_SIGNATURE_LIST *) CACert;
+  while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+    Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+    CertCount  = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+    for (Index = 0; Index < CertCount; Index++) {
+      //
+      // EfiTlsConfigDataTypeCACertificate
+      //
+      Status = HttpInstance->TlsConfiguration->SetData (
+                                                 HttpInstance->TlsConfiguration,
+                                                 EfiTlsConfigDataTypeCACertificate,
+                                                 Cert->SignatureData,
+                                                 CertList->SignatureSize - sizeof (Cert->SignatureOwner)
+                                                 );
+      if (EFI_ERROR (Status)) {
+        FreePool (CACert);
+        return Status;
+      }
+
+      Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+    }
+
+    ItemDataSize -= CertList->SignatureListSize;
+    CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+  }
+
+  FreePool (CACert);
+  return Status;
+}
+
+/**
+  Configure TLS session data.
+
+  @param[in, out]  HttpInstance       The HTTP instance private data.
+
+  @retval EFI_SUCCESS            TLS session data is configured.
+  @retval Others                 Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigureSession (
+  IN OUT HTTP_PROTOCOL      *HttpInstance
+  )
+{
+  EFI_STATUS                 Status;
+
+  //
+  // TlsConfigData initialization
+  //
+  HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient;
+  HttpInstance->TlsConfigData.VerifyMethod = EFI_TLS_VERIFY_PEER;
+  HttpInstance->TlsConfigData.SessionState = EfiTlsSessionNotStarted;
+
+  //
+  // EfiTlsConnectionEnd,
+  // EfiTlsVerifyMethod
+  // EfiTlsSessionState
+  //
+  Status = HttpInstance->Tls->SetSessionData (
+                                HttpInstance->Tls,
+                                EfiTlsConnectionEnd,
+                                &(HttpInstance->TlsConfigData.ConnectionEnd),
+                                sizeof (EFI_TLS_CONNECTION_END)
+                                );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = HttpInstance->Tls->SetSessionData (
+                                HttpInstance->Tls,
+                                EfiTlsVerifyMethod,
+                                &HttpInstance->TlsConfigData.VerifyMethod,
+                                sizeof (EFI_TLS_VERIFY)
+                                );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = HttpInstance->Tls->SetSessionData (
+                                HttpInstance->Tls,
+                                EfiTlsSessionState,
+                                &(HttpInstance->TlsConfigData.SessionState),
+                                sizeof (EFI_TLS_SESSION_STATE)
+                                );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Tls Config Certificate
+  //
+  Status = TlsConfigCertificate (HttpInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n"));
+    return Status;
+  }
+
+  //
+  // TlsCreateTxRxEvent
+  //
+  Status = TlsCreateTxRxEvent (HttpInstance);
+  if (EFI_ERROR (Status)) {
+    goto ERROR;
+  }
+
+  return Status;
+
+ERROR:
+  TlsCloseTxRxEvent (HttpInstance);
+
+  return Status;
+}
+
+/**
+  Transmit the Packet by processing the associated HTTPS token.
+
+  @param[in, out]   HttpInstance    Pointer to HTTP_PROTOCOL structure.
+  @param[in]        Packet          The packet to transmit.
+
+  @retval EFI_SUCCESS            The packet is transmitted.
+  @retval EFI_INVALID_PARAMETER  HttpInstance is NULL or Packet is NULL.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonTransmit (
+  IN OUT HTTP_PROTOCOL      *HttpInstance,
+  IN     NET_BUF            *Packet
+  )
+{
+  EFI_STATUS                Status;
+  VOID                      *Data;
+  UINTN                     Size;
+
+  if ((HttpInstance == NULL) || (Packet == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!HttpInstance->LocalAddressIsIPv6) {
+    Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +
+           (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);
+  } else {
+    Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +
+           (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);
+  }
+
+  Data = AllocatePool (Size);
+  if (Data == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  if (!HttpInstance->LocalAddressIsIPv6) {
+    ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push        = TRUE;
+    ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent      = FALSE;
+    ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength  = Packet->TotalSize;
+
+    //
+    // Build the fragment table.
+    //
+    ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
+
+    NetbufBuildExt (
+      Packet,
+      (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],
+      &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount
+      );
+
+    HttpInstance->Tcp4TlsTxToken.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;
+
+    Status = EFI_DEVICE_ERROR;
+
+    //
+    // Transmit the packet.
+    //
+    Status  = HttpInstance->Tcp4->Transmit (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsTxToken);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    while (!HttpInstance->TlsIsTxDone) {
+      HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
+    }
+
+    HttpInstance->TlsIsTxDone = FALSE;
+    Status = HttpInstance->Tcp4TlsTxToken.CompletionToken.Status;
+  } else {
+    ((EFI_TCP6_TRANSMIT_DATA *) Data)->Push        = TRUE;
+    ((EFI_TCP6_TRANSMIT_DATA *) Data)->Urgent      = FALSE;
+    ((EFI_TCP6_TRANSMIT_DATA *) Data)->DataLength  = Packet->TotalSize;
+
+    //
+    // Build the fragment table.
+    //
+    ((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
+
+    NetbufBuildExt (
+      Packet,
+      (NET_FRAGMENT *) &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentTable[0],
+      &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount
+      );
+
+    HttpInstance->Tcp6TlsTxToken.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;
+
+    Status = EFI_DEVICE_ERROR;
+
+    //
+    // Transmit the packet.
+    //
+    Status  = HttpInstance->Tcp6->Transmit (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsTxToken);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    while (!HttpInstance->TlsIsTxDone) {
+      HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
+    }
+
+    HttpInstance->TlsIsTxDone = FALSE;
+    Status = HttpInstance->Tcp6TlsTxToken.CompletionToken.Status;
+  }
+
+ON_EXIT:
+  FreePool (Data);
+
+  return Status;
+}
+
+/**
+  Receive the Packet by processing the associated HTTPS token.
+
+  @param[in, out]   HttpInstance    Pointer to HTTP_PROTOCOL structure.
+  @param[in]        Packet          The packet to transmit.
+  @param[in]        Timeout         The time to wait for connection done.
+
+  @retval EFI_SUCCESS            The Packet is received.
+  @retval EFI_INVALID_PARAMETER  HttpInstance is NULL or Packet is NULL.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval EFI_TIMEOUT            The operation is time out.
+  @retval Others                 Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonReceive (
+  IN OUT HTTP_PROTOCOL      *HttpInstance,
+  IN     NET_BUF            *Packet,
+  IN     EFI_EVENT          Timeout
+  )
+{
+  EFI_TCP4_RECEIVE_DATA     *Tcp4RxData;
+  EFI_TCP6_RECEIVE_DATA     *Tcp6RxData;
+  EFI_STATUS                Status;
+  NET_FRAGMENT              *Fragment;
+  UINT32                    FragmentCount;
+  UINT32                    CurrentFragment;
+
+  Tcp4RxData = NULL;
+  Tcp6RxData = NULL;
+
+  if ((HttpInstance == NULL) || (Packet == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FragmentCount = Packet->BlockOpNum;
+  Fragment      = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
+  if (Fragment == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  //
+  // Build the fragment table.
+  //
+  NetbufBuildExt (Packet, Fragment, &FragmentCount);
+
+  if (!HttpInstance->LocalAddressIsIPv6) {
+    Tcp4RxData = HttpInstance->Tcp4TlsRxToken.Packet.RxData;
+    if (Tcp4RxData == NULL) {
+      return EFI_INVALID_PARAMETER;
+    }
+    Tcp4RxData->FragmentCount         = 1;
+  } else {
+    Tcp6RxData = HttpInstance->Tcp6TlsRxToken.Packet.RxData;
+    if (Tcp6RxData == NULL) {
+      return EFI_INVALID_PARAMETER;
+    }
+    Tcp6RxData->FragmentCount         = 1;
+  }
+
+  CurrentFragment               = 0;
+  Status                        = EFI_SUCCESS;
+
+  while (CurrentFragment < FragmentCount) {
+    if (!HttpInstance->LocalAddressIsIPv6) {
+      Tcp4RxData->DataLength                       = Fragment[CurrentFragment].Len;
+      Tcp4RxData->FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;
+      Tcp4RxData->FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;
+      Status = HttpInstance->Tcp4->Receive (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken);
+    } else {
+      Tcp6RxData->DataLength                       = Fragment[CurrentFragment].Len;
+      Tcp6RxData->FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;
+      Tcp6RxData->FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;
+      Status = HttpInstance->Tcp6->Receive (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken);
+    }
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    while (!HttpInstance->TlsIsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+      //
+      // Poll until some data is received or an error occurs.
+      //
+      if (!HttpInstance->LocalAddressIsIPv6) {
+        HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
+      } else {
+        HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
+      }
+    }
+
+    if (!HttpInstance->TlsIsRxDone) {
+      //
+      // Timeout occurs, cancel the receive request.
+      //
+      if (!HttpInstance->LocalAddressIsIPv6) {
+        HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
+      } else {
+        HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
+      }
+
+      Status = EFI_TIMEOUT;
+      goto ON_EXIT;
+    } else {
+      HttpInstance->TlsIsRxDone = FALSE;
+    }
+
+    if (!HttpInstance->LocalAddressIsIPv6) {
+      Status = HttpInstance->Tcp4TlsRxToken.CompletionToken.Status;
+      if (EFI_ERROR (Status)) {
+        goto ON_EXIT;
+      }
+
+      Fragment[CurrentFragment].Len -= Tcp4RxData->FragmentTable[0].FragmentLength;
+      if (Fragment[CurrentFragment].Len == 0) {
+        CurrentFragment++;
+      } else {
+        Fragment[CurrentFragment].Bulk += Tcp4RxData->FragmentTable[0].FragmentLength;
+      }
+    } else {
+      Status = HttpInstance->Tcp6TlsRxToken.CompletionToken.Status;
+      if (EFI_ERROR (Status)) {
+        goto ON_EXIT;
+      }
+
+      Fragment[CurrentFragment].Len -= Tcp6RxData->FragmentTable[0].FragmentLength;
+      if (Fragment[CurrentFragment].Len == 0) {
+        CurrentFragment++;
+      } else {
+        Fragment[CurrentFragment].Bulk += Tcp6RxData->FragmentTable[0].FragmentLength;
+      }
+    }
+  }
+
+ON_EXIT:
+
+  if (Fragment != NULL) {
+    FreePool (Fragment);
+  }
+
+  return Status;
+}
+
+/**
+  Receive one TLS PDU. An TLS PDU contains an TLS record header and it's
+  corresponding record data. These two parts will be put into two blocks of buffers in the
+  net buffer.
+
+  @param[in, out]      HttpInstance    Pointer to HTTP_PROTOCOL structure.
+  @param[out]          Pdu             The received TLS PDU.
+  @param[in]           Timeout         The time to wait for connection done.
+
+  @retval EFI_SUCCESS          An TLS PDU is received.
+  @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+  @retval EFI_PROTOCOL_ERROR   An unexpected TLS packet was received.
+  @retval Others               Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsReceiveOnePdu (
+  IN OUT HTTP_PROTOCOL      *HttpInstance,
+     OUT NET_BUF            **Pdu,
+  IN     EFI_EVENT          Timeout
+  )
+{
+  EFI_STATUS      Status;
+
+  LIST_ENTRY      *NbufList;
+
+  UINT32          Len;
+
+  NET_BUF           *PduHdr;
+  UINT8             *Header;
+  TLS_RECORD_HEADER RecordHeader;
+
+  NET_BUF           *DataSeg;
+
+  NbufList = NULL;
+  PduHdr   = NULL;
+  Header   = NULL;
+  DataSeg  = NULL;
+
+  NbufList = AllocatePool (sizeof (LIST_ENTRY));
+  if (NbufList == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InitializeListHead (NbufList);
+
+  //
+  // Allocate buffer to receive one TLS header.
+  //
+  Len     = sizeof (TLS_RECORD_HEADER);
+  PduHdr  = NetbufAlloc (Len);
+  if (PduHdr == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
+  if (Header == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  //
+  // First step, receive one TLS header.
+  //
+  Status = TlsCommonReceive (HttpInstance, PduHdr, Timeout);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  RecordHeader = *(TLS_RECORD_HEADER *) Header;
+  if ((RecordHeader.ContentType == TLS_CONTENT_TYPE_HANDSHAKE ||
+    RecordHeader.ContentType == TLS_CONTENT_TYPE_ALERT ||
+    RecordHeader.ContentType == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC ||
+    RecordHeader.ContentType == TLS_CONTENT_TYPE_APPLICATION_DATA) &&
+    (RecordHeader.Version.Major == 0x03) && /// Major versions are same.
+    (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor ==TLS11_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+   ) {
+    InsertTailList (NbufList, &PduHdr->List);
+  } else {
+    Status = EFI_PROTOCOL_ERROR;
+    goto ON_EXIT;
+  }
+
+  Len = SwapBytes16(RecordHeader.Length);
+  if (Len == 0) {
+    //
+    // No TLS payload.
+    //
+    goto FORM_PDU;
+  }
+
+  //
+  // Allocate buffer to receive one TLS payload.
+  //
+  DataSeg = NetbufAlloc (Len);
+  if (DataSeg == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
+
+  //
+  // Second step, receive one TLS payload.
+  //
+  Status = TlsCommonReceive (HttpInstance, DataSeg, Timeout);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  InsertTailList (NbufList, &DataSeg->List);
+
+FORM_PDU:
+  //
+  // Form the PDU from a list of PDU.
+  //
+  *Pdu = NetbufFromBufList (NbufList, 0, 0, FreeNbufList, NbufList);
+  if (*Pdu == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+  }
+
+ON_EXIT:
+
+  if (EFI_ERROR (Status)) {
+    //
+    // Free the Nbufs in this NbufList and the NbufList itself.
+    //
+    FreeNbufList (NbufList);
+  }
+
+  return Status;
+}
+
+/**
+  Connect one TLS session by finishing the TLS handshake process.
+
+  @param[in]  HttpInstance       The HTTP instance private data.
+  @param[in]  Timeout            The time to wait for connection done.
+
+  @retval EFI_SUCCESS            The TLS session is established.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval EFI_ABORTED            TLS session state is incorrect.
+  @retval Others                 Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConnectSession (
+  IN  HTTP_PROTOCOL            *HttpInstance,
+  IN  EFI_EVENT                Timeout
+  )
+{
+  EFI_STATUS              Status;
+  UINT8                   *BufferOut;
+  UINTN                   BufferOutSize;
+  NET_BUF                 *PacketOut;
+  UINT8                   *DataOut;
+  NET_BUF                 *Pdu;
+  UINT8                   *BufferIn;
+  UINTN                   BufferInSize;
+  UINT8                   *GetSessionDataBuffer;
+  UINTN                   GetSessionDataBufferSize;
+
+  BufferOut    = NULL;
+  PacketOut    = NULL;
+  DataOut      = NULL;
+  Pdu          = NULL;
+  BufferIn     = NULL;
+
+  //
+  // Initialize TLS state.
+  //
+  HttpInstance->TlsSessionState = EfiTlsSessionNotStarted;
+  Status = HttpInstance->Tls->SetSessionData (
+                                HttpInstance->Tls,
+                                EfiTlsSessionState,
+                                &(HttpInstance->TlsSessionState),
+                                sizeof (EFI_TLS_SESSION_STATE)
+                                );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Create ClientHello
+  //
+  BufferOutSize = DEF_BUF_LEN;
+  BufferOut = AllocateZeroPool (BufferOutSize);
+  if (BufferOut == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    return Status;
+  }
+
+  Status = HttpInstance->Tls->BuildResponsePacket (
+                                HttpInstance->Tls,
+                                NULL,
+                                0,
+                                BufferOut,
+                                &BufferOutSize
+                                );
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    FreePool (BufferOut);
+    BufferOut = AllocateZeroPool (BufferOutSize);
+    if (BufferOut == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->BuildResponsePacket (
+                                  HttpInstance->Tls,
+                                  NULL,
+                                  0,
+                                  BufferOut,
+                                  &BufferOutSize
+                                  );
+  }
+  if (EFI_ERROR (Status)) {
+    FreePool (BufferOut);
+    return Status;
+  }
+
+  //
+  // Transmit ClientHello
+  //
+  PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+  DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+  CopyMem (DataOut, BufferOut, BufferOutSize);
+  Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+  FreePool (BufferOut);
+  NetbufFree (PacketOut);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \
+    ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+    //
+    // Receive one TLS record.
+    //
+    Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    BufferInSize = Pdu->TotalSize;
+    BufferIn = AllocateZeroPool (BufferInSize);
+    if (BufferIn == NULL) {
+      NetbufFree (Pdu);
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn);
+
+    NetbufFree (Pdu);
+
+    //
+    // Handle Receive data.
+    //
+    BufferOutSize = DEF_BUF_LEN;
+    BufferOut = AllocateZeroPool (BufferOutSize);
+    if (BufferOut == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->BuildResponsePacket (
+                                  HttpInstance->Tls,
+                                  BufferIn,
+                                  BufferInSize,
+                                  BufferOut,
+                                  &BufferOutSize
+                                  );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+       FreePool (BufferOut);
+       BufferOut = AllocateZeroPool (BufferOutSize);
+       if (BufferOut == NULL) {
+         FreePool (BufferIn);
+         Status = EFI_OUT_OF_RESOURCES;
+         return Status;
+       }
+
+       Status = HttpInstance->Tls->BuildResponsePacket (
+                                     HttpInstance->Tls,
+                                     BufferIn,
+                                     BufferInSize,
+                                     BufferOut,
+                                     &BufferOutSize
+                                     );
+    }
+
+    FreePool (BufferIn);
+
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    if (BufferOutSize != 0) {
+      //
+      // Transmit the response packet.
+      //
+      PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+      DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+      CopyMem (DataOut, BufferOut, BufferOutSize);
+
+      Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+      NetbufFree (PacketOut);
+
+      if (EFI_ERROR (Status)) {
+        FreePool (BufferOut);
+        return Status;
+      }
+    }
+
+    FreePool (BufferOut);
+
+    //
+    // Get the session state, then decide whether need to continue handle received packet.
+    //
+    GetSessionDataBufferSize = DEF_BUF_LEN;
+    GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+    if (GetSessionDataBuffer == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->GetSessionData (
+                                  HttpInstance->Tls,
+                                  EfiTlsSessionState,
+                                  GetSessionDataBuffer,
+                                  &GetSessionDataBufferSize
+                                  );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+       FreePool (GetSessionDataBuffer);
+       GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+       if (GetSessionDataBuffer == NULL) {
+         Status = EFI_OUT_OF_RESOURCES;
+         return Status;
+       }
+
+       Status = HttpInstance->Tls->GetSessionData (
+                                     HttpInstance->Tls,
+                                     EfiTlsSessionState,
+                                     GetSessionDataBuffer,
+                                     &GetSessionDataBufferSize
+                                     );
+    }
+    if (EFI_ERROR (Status)) {
+      FreePool(GetSessionDataBuffer);
+      return Status;
+    }
+
+    ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE));
+    HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer;
+
+    FreePool (GetSessionDataBuffer);
+
+    if(HttpInstance->TlsSessionState == EfiTlsSessionError) {
+      return EFI_ABORTED;
+    }
+  }
+
+  if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) {
+    Status = EFI_ABORTED;
+  }
+
+  return Status;
+}
+
+/**
+  Close the TLS session and send out the close notification message.
+
+  @param[in]  HttpInstance       The HTTP instance private data.
+
+  @retval EFI_SUCCESS            The TLS session is closed.
+  @retval EFI_INVALID_PARAMETER  HttpInstance is NULL.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval Others                 Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCloseSession (
+  IN  HTTP_PROTOCOL            *HttpInstance
+  )
+{
+  EFI_STATUS      Status;
+
+  UINT8           *BufferOut;
+  UINTN           BufferOutSize;
+
+  NET_BUF         *PacketOut;
+  UINT8           *DataOut;
+
+  Status    = EFI_SUCCESS;
+  BufferOut = NULL;
+  PacketOut = NULL;
+  DataOut   = NULL;
+
+  if (HttpInstance == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  HttpInstance->TlsSessionState = EfiTlsSessionClosing;
+
+  Status = HttpInstance->Tls->SetSessionData (
+                                HttpInstance->Tls,
+                                EfiTlsSessionState,
+                                &(HttpInstance->TlsSessionState),
+                                sizeof (EFI_TLS_SESSION_STATE)
+                                );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  BufferOutSize = DEF_BUF_LEN;
+  BufferOut = AllocateZeroPool (BufferOutSize);
+  if (BufferOut == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    return Status;
+  }
+
+  Status = HttpInstance->Tls->BuildResponsePacket (
+                                HttpInstance->Tls,
+                                NULL,
+                                0,
+                                BufferOut,
+                                &BufferOutSize
+                                );
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    FreePool (BufferOut);
+    BufferOut = AllocateZeroPool (BufferOutSize);
+    if (BufferOut == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->BuildResponsePacket (
+                                  HttpInstance->Tls,
+                                  NULL,
+                                  0,
+                                  BufferOut,
+                                  &BufferOutSize
+                                  );
+  }
+
+  if (EFI_ERROR (Status)) {
+    FreePool (BufferOut);
+    return Status;
+  }
+
+  PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+  DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+  CopyMem (DataOut, BufferOut, BufferOutSize);
+
+  Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+  FreePool (BufferOut);
+  NetbufFree (PacketOut);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return Status;
+}
+
+/**
+  Process one message according to the CryptMode.
+
+  @param[in]           HttpInstance    Pointer to HTTP_PROTOCOL structure.
+  @param[in]           Message         Pointer to the message buffer needed to processed.
+  @param[in]           MessageSize     Pointer to the message buffer size.
+  @param[in]           ProcessMode     Process mode.
+  @param[in, out]      Fragment        Only one Fragment returned after the Message is
+                                       processed successfully.
+
+  @retval EFI_SUCCESS          Message is processed successfully.
+  @retval EFI_OUT_OF_RESOURCES   Can't allocate memory resources.
+  @retval Others               Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsProcessMessage (
+  IN     HTTP_PROTOCOL            *HttpInstance,
+  IN     UINT8                    *Message,
+  IN     UINTN                    MessageSize,
+  IN     EFI_TLS_CRYPT_MODE       ProcessMode,
+  IN OUT NET_FRAGMENT             *Fragment
+  )
+{
+  EFI_STATUS                      Status;
+  UINT8                           *Buffer;
+  UINT32                          BufferSize;
+  UINT32                          BytesCopied;
+  EFI_TLS_FRAGMENT_DATA           *FragmentTable;
+  UINT32                          FragmentCount;
+  EFI_TLS_FRAGMENT_DATA           *OriginalFragmentTable;
+  UINTN                           Index;
+
+  Status                   = EFI_SUCCESS;
+  Buffer                   = NULL;
+  BufferSize               = 0;
+  BytesCopied              = 0;
+  FragmentTable            = NULL;
+  OriginalFragmentTable    = NULL;
+
+  //
+  // Rebuild fragment table from BufferIn.
+  //
+  FragmentCount = 1;
+  FragmentTable = AllocateZeroPool (FragmentCount * sizeof (EFI_TLS_FRAGMENT_DATA));
+  if (FragmentTable == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  FragmentTable->FragmentLength = (UINT32) MessageSize;
+  FragmentTable->FragmentBuffer = Message;
+
+  //
+  // Record the original FragmentTable.
+  //
+  OriginalFragmentTable = FragmentTable;
+
+  //
+  // Process the Message.
+  //
+  Status = HttpInstance->Tls->ProcessPacket (
+                                HttpInstance->Tls,
+                                &FragmentTable,
+                                &FragmentCount,
+                                ProcessMode
+                                );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Calculate the size according to FragmentTable.
+  //
+  for (Index = 0; Index < FragmentCount; Index++) {
+    BufferSize += FragmentTable[Index].FragmentLength;
+  }
+
+  //
+  // Allocate buffer for processed data.
+  //
+  Buffer = AllocateZeroPool (BufferSize);
+  if (Buffer == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_EXIT;
+  }
+
+  //
+  // Copy the new FragmentTable buffer into Buffer.
+  //
+  for (Index = 0; Index < FragmentCount; Index++) {
+    CopyMem (
+      (Buffer + BytesCopied),
+      FragmentTable[Index].FragmentBuffer,
+      FragmentTable[Index].FragmentLength
+      );
+    BytesCopied += FragmentTable[Index].FragmentLength;
+
+    //
+    // Free the FragmentBuffer since it has been copied.
+    //
+    FreePool (FragmentTable[Index].FragmentBuffer);
+  }
+
+  Fragment->Len  = BufferSize;
+  Fragment->Bulk = Buffer;
+
+ON_EXIT:
+
+  if (OriginalFragmentTable != NULL) {
+    FreePool (OriginalFragmentTable);
+    OriginalFragmentTable = NULL;
+  }
+
+  //
+  // Caller has the responsibility to free the FragmentTable.
+  //
+  if (FragmentTable != NULL) {
+    FreePool (FragmentTable);
+    FragmentTable = NULL;
+  }
+
+  return Status;
+}
+
+/**
+  Receive one fragment decrypted from one TLS record.
+
+  @param[in]           HttpInstance    Pointer to HTTP_PROTOCOL structure.
+  @param[in, out]      Fragment        The received Fragment.
+  @param[in]           Timeout         The time to wait for connection done.
+
+  @retval EFI_SUCCESS          One fragment is received.
+  @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+  @retval EFI_ABORTED          Something wrong decryption the message.
+  @retval Others               Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpsReceive (
+  IN     HTTP_PROTOCOL         *HttpInstance,
+  IN OUT NET_FRAGMENT          *Fragment,
+  IN     EFI_EVENT             Timeout
+  )
+{
+  EFI_STATUS                      Status;
+  NET_BUF                         *Pdu;
+  TLS_RECORD_HEADER               RecordHeader;
+  UINT8                           *BufferIn;
+  UINTN                           BufferInSize;
+  NET_FRAGMENT                    TempFragment;
+  UINT8                           *BufferOut;
+  UINTN                           BufferOutSize;
+  NET_BUF                         *PacketOut;
+  UINT8                           *DataOut;
+  UINT8                           *GetSessionDataBuffer;
+  UINTN                           GetSessionDataBufferSize;
+
+  Status                   = EFI_SUCCESS;
+  Pdu                      = NULL;
+  BufferIn                 = NULL;
+  BufferInSize             = 0;
+  BufferOut                = NULL;
+  BufferOutSize            = 0;
+  PacketOut                = NULL;
+  DataOut                  = NULL;
+  GetSessionDataBuffer     = NULL;
+  GetSessionDataBufferSize = 0;
+
+  //
+  // Receive only one TLS record
+  //
+  Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  BufferInSize = Pdu->TotalSize;
+  BufferIn = AllocateZeroPool (BufferInSize);
+  if (BufferIn == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    NetbufFree (Pdu);
+    return Status;
+  }
+
+  NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn);
+
+  NetbufFree (Pdu);
+
+  //
+  // Handle Receive data.
+  //
+  RecordHeader = *(TLS_RECORD_HEADER *) BufferIn;
+
+  if ((RecordHeader.ContentType == TLS_CONTENT_TYPE_APPLICATION_DATA) &&
+    (RecordHeader.Version.Major == 0x03) &&
+    (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+  ) {
+    //
+    // Decrypt Packet.
+    //
+    Status = TlsProcessMessage (
+               HttpInstance,
+               BufferIn,
+               BufferInSize,
+               EfiTlsDecrypt,
+               &TempFragment
+               );
+
+    FreePool (BufferIn);
+
+    if (EFI_ERROR (Status)) {
+      if (Status == EFI_ABORTED) {
+        //
+        // Something wrong decryption the message.
+        // BuildResponsePacket() will be called to generate Error Alert message and send it out.
+        //
+        BufferOutSize = DEF_BUF_LEN;
+        BufferOut = AllocateZeroPool (BufferOutSize);
+        if (BufferOut == NULL) {
+          Status = EFI_OUT_OF_RESOURCES;
+          return Status;
+        }
+
+        Status = HttpInstance->Tls->BuildResponsePacket (
+                                      HttpInstance->Tls,
+                                      NULL,
+                                      0,
+                                      BufferOut,
+                                      &BufferOutSize
+                                      );
+        if (Status == EFI_BUFFER_TOO_SMALL) {
+          FreePool (BufferOut);
+          BufferOut = AllocateZeroPool (BufferOutSize);
+          if (BufferOut == NULL) {
+            Status = EFI_OUT_OF_RESOURCES;
+            return Status;
+          }
+
+          Status = HttpInstance->Tls->BuildResponsePacket (
+                                        HttpInstance->Tls,
+                                        NULL,
+                                        0,
+                                        BufferOut,
+                                        &BufferOutSize
+                                        );
+        }
+        if (EFI_ERROR (Status)) {
+          FreePool(BufferOut);
+          return Status;
+        }
+
+        if (BufferOutSize != 0) {
+          PacketOut = NetbufAlloc ((UINT32)BufferOutSize);
+          DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+          CopyMem (DataOut, BufferOut, BufferOutSize);
+
+          Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+          NetbufFree (PacketOut);
+        }
+
+        FreePool(BufferOut);
+
+        if (EFI_ERROR (Status)) {
+          return Status;
+        }
+
+        return EFI_ABORTED;
+      }
+
+      return Status;
+    }
+
+    //
+    // Parsing buffer.
+    //
+    ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TLS_CONTENT_TYPE_APPLICATION_DATA);
+
+    BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length;
+    BufferIn = AllocateZeroPool (BufferInSize);
+    if (BufferIn == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    CopyMem (BufferIn, TempFragment.Bulk + sizeof (TLS_RECORD_HEADER), BufferInSize);
+
+    //
+    // Free the buffer in TempFragment.
+    //
+    FreePool (TempFragment.Bulk);
+
+  } else if ((RecordHeader.ContentType == TLS_CONTENT_TYPE_ALERT) &&
+    (RecordHeader.Version.Major == 0x03) &&
+    (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR ||
+    RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+    ) {
+    BufferOutSize = DEF_BUF_LEN;
+    BufferOut = AllocateZeroPool (BufferOutSize);
+    if (BufferOut == NULL) {
+      FreePool (BufferIn);
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->BuildResponsePacket (
+                                  HttpInstance->Tls,
+                                  BufferIn,
+                                  BufferInSize,
+                                  BufferOut,
+                                  &BufferOutSize
+                                  );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+      FreePool (BufferOut);
+      BufferOut = AllocateZeroPool (BufferOutSize);
+      if (BufferOut == NULL) {
+        FreePool (BufferIn);
+        Status = EFI_OUT_OF_RESOURCES;
+        return Status;
+      }
+
+      Status = HttpInstance->Tls->BuildResponsePacket (
+                                    HttpInstance->Tls,
+                                    BufferIn,
+                                    BufferInSize,
+                                    BufferOut,
+                                    &BufferOutSize
+                                    );
+    }
+
+    FreePool (BufferIn);
+
+    if (EFI_ERROR (Status)) {
+      FreePool (BufferOut);
+      return Status;
+    }
+
+    if (BufferOutSize != 0) {
+      PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+      DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+      CopyMem (DataOut, BufferOut, BufferOutSize);
+
+      Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+      NetbufFree (PacketOut);
+    }
+
+    FreePool (BufferOut);
+
+    //
+    // Get the session state.
+    //
+    GetSessionDataBufferSize = DEF_BUF_LEN;
+    GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+    if (GetSessionDataBuffer == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      return Status;
+    }
+
+    Status = HttpInstance->Tls->GetSessionData (
+                                  HttpInstance->Tls,
+                                  EfiTlsSessionState,
+                                  GetSessionDataBuffer,
+                                  &GetSessionDataBufferSize
+                                  );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+       FreePool (GetSessionDataBuffer);
+       GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+       if (GetSessionDataBuffer == NULL) {
+         Status = EFI_OUT_OF_RESOURCES;
+         return Status;
+       }
+
+       Status = HttpInstance->Tls->GetSessionData (
+                                     HttpInstance->Tls,
+                                     EfiTlsSessionState,
+                                     GetSessionDataBuffer,
+                                     &GetSessionDataBufferSize
+                                     );
+    }
+    if (EFI_ERROR (Status)) {
+      FreePool (GetSessionDataBuffer);
+      return Status;
+    }
+
+    ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE));
+    HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer;
+
+    FreePool (GetSessionDataBuffer);
+
+    if(HttpInstance->TlsSessionState == EfiTlsSessionError) {
+      DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n"));
+      return EFI_ABORTED;
+    }
+
+    BufferIn = NULL;
+    BufferInSize = 0;
+  }
+
+  Fragment->Bulk = BufferIn;
+  Fragment->Len = (UINT32) BufferInSize;
+
+  return Status;
+}