/** @file
  The Miscellaneous Routines for WiFi Connection Manager.

  Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "WifiConnectionMgrDxe.h"

//
//  STA AKM preference order
//  REF: https://www.wi-fi.org/file/wpa3-specification
//
STATIC UINT32  mAKMSuitePreference[] = {
  IEEE_80211_AKM_SUITE_8021X_SUITE_B192,            // AKM Suite 12
  IEEE_80211_AKM_SUITE_8021X_SUITE_B,               // AKM Suite 11
  IEEE_80211_AKM_SUITE_8021X_OR_PMKSA_SHA256,       // AKM Suite 5
  IEEE_80211_AKM_SUITE_8021X_OR_PMKSA,              // AKM Suite 1

  IEEE_80211_AKM_SUITE_SAE,                         // AKM Suite 8
  IEEE_80211_AKM_SUITE_PSK_SHA256,                  // AKM Suite 6
  IEEE_80211_AKM_SUITE_PSK,                         // AKM Suite 2

  IEEE_80211_AKM_SUITE_OWE                          // AKM Suite 18
};
#define AKM_SUITE_PREFERENCE_COUNT  (sizeof (mAKMSuitePreference)  / sizeof (UINT32))

/**
  Empty function for event process function.

  @param Event    The Event need to be process
  @param Context  The context of the event.

**/
VOID
EFIAPI
WifiMgrInternalEmptyFunction (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  )
{
  return;
}

/**
  Convert the mac address into a hexadecimal encoded ":" seperated string.

  @param[in]  Mac     The mac address.
  @param[in]  StrSize The size, in bytes, of the output buffer specified by Str.
  @param[out] Str     The storage to return the mac string.

**/
VOID
WifiMgrMacAddrToStr (
  IN  EFI_80211_MAC_ADDRESS  *Mac,
  IN  UINT32                 StrSize,
  OUT CHAR16                 *Str
  )
{
  if ((Mac == NULL) || (Str == NULL)) {
    return;
  }

  UnicodeSPrint (
    Str,
    StrSize,
    L"%02X:%02X:%02X:%02X:%02X:%02X",
    Mac->Addr[0],
    Mac->Addr[1],
    Mac->Addr[2],
    Mac->Addr[3],
    Mac->Addr[4],
    Mac->Addr[5]
    );
}

/**
  Read private key file to buffer.

  @param[in]   FileContext           The file context of private key file.
  @param[out]  PrivateKeyDataAddr    The buffer address to restore private key file, should be
                                     freed by caller.
  @param[out]  PrivateKeyDataSize    The size of read private key file.

  @retval EFI_SUCCESS                Successfully read the private key file.
  @retval EFI_INVALID_PARAMETER      One or more of the parameters is invalid.

**/
EFI_STATUS
WifiMgrReadFileToBuffer (
  IN   WIFI_MGR_FILE_CONTEXT  *FileContext,
  OUT  VOID                   **DataAddr,
  OUT  UINTN                  *DataSize
  )
{
  EFI_STATUS  Status;

  if ((FileContext != NULL) && (FileContext->FHandle != NULL)) {
    Status = ReadFileContent (
               FileContext->FHandle,
               DataAddr,
               DataSize,
               0
               );

    if (FileContext->FHandle != NULL) {
      FileContext->FHandle->Close (FileContext->FHandle);
    }

    FileContext->FHandle = NULL;
    return Status;
  }

  return EFI_INVALID_PARAMETER;
}

/**
  Get the Nic data by the NicIndex.

  @param[in]  Private        The pointer to the global private data structure.
  @param[in]  NicIndex       The index indicates the position of wireless NIC.

  @return     Pointer to the Nic data, or NULL if not found.

**/
WIFI_MGR_DEVICE_DATA *
WifiMgrGetNicByIndex (
  IN WIFI_MGR_PRIVATE_DATA  *Private,
  IN UINT32                 NicIndex
  )
{
  LIST_ENTRY            *Entry;
  WIFI_MGR_DEVICE_DATA  *Nic;

  if (Private == NULL) {
    return NULL;
  }

  NET_LIST_FOR_EACH (Entry, &Private->NicList) {
    Nic = NET_LIST_USER_STRUCT_S (
            Entry,
            WIFI_MGR_DEVICE_DATA,
            Link,
            WIFI_MGR_DEVICE_DATA_SIGNATURE
            );
    if (Nic->NicIndex == NicIndex) {
      return Nic;
    }
  }

  return NULL;
}

/**
  Find a network profile through its' SSId and securit type, and the SSId is an unicode string.

  @param[in]  SSId                   The target network's SSId.
  @param[in]  SecurityType           The target network's security type.
  @param[in]  ProfileList            The profile list on a Nic.

  @return Pointer to a network profile, or NULL if not found.

**/
WIFI_MGR_NETWORK_PROFILE *
WifiMgrGetProfileByUnicodeSSId (
  IN  CHAR16      *SSId,
  IN  UINT8       SecurityType,
  IN  LIST_ENTRY  *ProfileList
  )
{
  LIST_ENTRY                *Entry;
  WIFI_MGR_NETWORK_PROFILE  *Profile;

  if ((SSId == NULL) || (ProfileList == NULL)) {
    return NULL;
  }

  NET_LIST_FOR_EACH (Entry, ProfileList) {
    Profile = NET_LIST_USER_STRUCT_S (
                Entry,
                WIFI_MGR_NETWORK_PROFILE,
                Link,
                WIFI_MGR_PROFILE_SIGNATURE
                );
    if ((StrCmp (SSId, Profile->SSId) == 0) && (SecurityType == Profile->SecurityType)) {
      return Profile;
    }
  }

  return NULL;
}

/**
  Find a network profile through its' SSId and securit type, and the SSId is an ascii string.

  @param[in]  SSId               The target network's SSId.
  @param[in]  SecurityType       The target network's security type.
  @param[in]  ProfileList        The profile list on a Nic.

  @return Pointer to a network profile, or NULL if not found.

**/
WIFI_MGR_NETWORK_PROFILE *
WifiMgrGetProfileByAsciiSSId (
  IN  CHAR8       *SSId,
  IN  UINT8       SecurityType,
  IN  LIST_ENTRY  *ProfileList
  )
{
  CHAR16  SSIdUniCode[SSID_STORAGE_SIZE];

  if (SSId == NULL) {
    return NULL;
  }

  if (AsciiStrToUnicodeStrS (SSId, SSIdUniCode, SSID_STORAGE_SIZE) != RETURN_SUCCESS) {
    return NULL;
  }

  return WifiMgrGetProfileByUnicodeSSId (SSIdUniCode, SecurityType, ProfileList);
}

/**
  Find a network profile through its' profile index.

  @param[in]  ProfileIndex           The target network's profile index.
  @param[in]  ProfileList            The profile list on a Nic.

  @return Pointer to a network profile, or NULL if not found.

**/
WIFI_MGR_NETWORK_PROFILE *
WifiMgrGetProfileByProfileIndex (
  IN  UINT32      ProfileIndex,
  IN  LIST_ENTRY  *ProfileList
  )
{
  WIFI_MGR_NETWORK_PROFILE  *Profile;
  LIST_ENTRY                *Entry;

  if (ProfileList == NULL) {
    return NULL;
  }

  NET_LIST_FOR_EACH (Entry, ProfileList) {
    Profile = NET_LIST_USER_STRUCT_S (
                Entry,
                WIFI_MGR_NETWORK_PROFILE,
                Link,
                WIFI_MGR_PROFILE_SIGNATURE
                );
    if (Profile->ProfileIndex == ProfileIndex) {
      return Profile;
    }
  }
  return NULL;
}

/**
  To test if the AKMSuite is in supported AKMSuite list.

  @param[in]  SupportedAKMSuiteCount       The count of the supported AKMSuites.
  @param[in]  SupportedAKMSuiteList        The supported AKMSuite list.
  @param[in]  AKMSuite                     The AKMSuite to be tested.

  @return True if this AKMSuite is supported, or False if not.

**/
BOOLEAN
WifiMgrSupportAKMSuite (
  IN  UINT16  SupportedAKMSuiteCount,
  IN  UINT32  *SupportedAKMSuiteList,
  IN  UINT32  *AKMSuite
  )
{
  UINT16  Index;

  if ((AKMSuite == NULL) || (SupportedAKMSuiteList == NULL) ||
      (SupportedAKMSuiteCount == 0))
  {
    return FALSE;
  }

  for (Index = 0; Index < SupportedAKMSuiteCount; Index++) {
    if (SupportedAKMSuiteList[Index] == *AKMSuite) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  To check if the CipherSuite is in supported CipherSuite list.

  @param[in]  SupportedCipherSuiteCount       The count of the supported CipherSuites.
  @param[in]  SupportedCipherSuiteList        The supported CipherSuite list.
  @param[in]  CipherSuite                     The CipherSuite to be tested.

  @return True if this CipherSuite is supported, or False if not.

**/
BOOLEAN
WifiMgrSupportCipherSuite (
  IN  UINT16  SupportedCipherSuiteCount,
  IN  UINT32  *SupportedCipherSuiteList,
  IN  UINT32  *CipherSuite
  )
{
  UINT16  Index;

  if ((CipherSuite == NULL) || (SupportedCipherSuiteCount == 0) ||
      (SupportedCipherSuiteList == NULL))
  {
    return FALSE;
  }

  for (Index = 0; Index < SupportedCipherSuiteCount; Index++) {
    if (SupportedCipherSuiteList[Index] == *CipherSuite) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  Check an AKM suite list and a Cipher suite list to see if one or more AKM suites or Cipher suites
  are supported and find the matchable security type.

  @param[in]   AKMList                     The target AKM suite list to be checked.
  @param[in]   CipherList                  The target Cipher suite list to be checked
  @param[in]   Nic                         The Nic to operate, contains the supported AKMSuite list
                                           and supported CipherSuite list
  @param[out]  SecurityType                To identify a security type from the AKM suite list and
                                           Cipher suite list
  @param[out]  AKMSuiteSupported           To identify if this security type is supported. If it is
                                           NULL, overcome this field
  @param[out]  CipherSuiteSupported        To identify if this security type is supported. If it is
                                           NULL, overcome this field

  @retval EFI_SUCCESS                      This operation has completed successfully.
  @retval EFI_INVALID_PARAMETER            No Nic found or the suite list is null.

**/
EFI_STATUS
WifiMgrCheckRSN (
  IN    EFI_80211_AKM_SUITE_SELECTOR     *AKMList,
  IN    EFI_80211_CIPHER_SUITE_SELECTOR  *CipherList,
  IN    WIFI_MGR_DEVICE_DATA             *Nic,
  OUT   UINT8                            *SecurityType,
  OUT   BOOLEAN                          *AKMSuiteSupported,
  OUT   BOOLEAN                          *CipherSuiteSupported
  )
{
  EFI_80211_AKM_SUITE_SELECTOR     *SupportedAKMSuites;
  EFI_80211_CIPHER_SUITE_SELECTOR  *SupportedSwCipherSuites;
  EFI_80211_CIPHER_SUITE_SELECTOR  *SupportedHwCipherSuites;
  UINT32                           *AKMSuite;
  EFI_80211_SUITE_SELECTOR         *CipherSuite;
  UINT16                           AKMIndex;
  UINT16                           CipherIndex;

  if ((Nic == NULL) || (AKMList == NULL) || (CipherList == NULL) || (SecurityType == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  SupportedAKMSuites      = Nic->SupportedSuites.SupportedAKMSuites;
  SupportedSwCipherSuites = Nic->SupportedSuites.SupportedSwCipherSuites;
  SupportedHwCipherSuites = Nic->SupportedSuites.SupportedHwCipherSuites;

  *SecurityType = SECURITY_TYPE_UNKNOWN;
  if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
    *AKMSuiteSupported    = FALSE;
    *CipherSuiteSupported = FALSE;
  }

  if (AKMList->AKMSuiteCount == 0) {
    if (CipherList->CipherSuiteCount == 0) {
      *SecurityType = SECURITY_TYPE_NONE;
      if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
        *AKMSuiteSupported    = TRUE;
        *CipherSuiteSupported = TRUE;
      }
    }

    return EFI_SUCCESS;
  }

  for (AKMIndex = 0; AKMIndex < AKM_SUITE_PREFERENCE_COUNT; AKMIndex++) {
    AKMSuite = mAKMSuitePreference + AKMIndex;
    if (WifiMgrSupportAKMSuite (AKMList->AKMSuiteCount, (UINT32 *)AKMList->AKMSuiteList, AKMSuite) &&
        WifiMgrSupportAKMSuite (SupportedAKMSuites->AKMSuiteCount, (UINT32 *)SupportedAKMSuites->AKMSuiteList, AKMSuite))
    {
      if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
        *AKMSuiteSupported = TRUE;
      }

      //
      // OWE transition mode allow CipherSuiteCount is 0
      //
      if (CipherList->CipherSuiteCount == 0) {
        *SecurityType = WifiMgrGetSecurityType ((UINT32 *)AKMSuite, NULL);
        if (*SecurityType != SECURITY_TYPE_UNKNOWN) {
          if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
            *CipherSuiteSupported = TRUE;
          }

          return EFI_SUCCESS;
        }
      }

      for (CipherIndex = 0; CipherIndex < CipherList->CipherSuiteCount; CipherIndex++) {
        CipherSuite = CipherList->CipherSuiteList + CipherIndex;

        if (SupportedSwCipherSuites != NULL) {
          if (WifiMgrSupportCipherSuite (
                SupportedSwCipherSuites->CipherSuiteCount,
                (UINT32 *)SupportedSwCipherSuites->CipherSuiteList,
                (UINT32 *)CipherSuite
                ))
          {
            *SecurityType = WifiMgrGetSecurityType ((UINT32 *)AKMSuite, (UINT32 *)CipherSuite);

            if (*SecurityType != SECURITY_TYPE_UNKNOWN) {
              if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
                *CipherSuiteSupported = TRUE;
              }

              return EFI_SUCCESS;
            }
          }
        }

        if (SupportedHwCipherSuites != NULL) {
          if (WifiMgrSupportCipherSuite (
                SupportedHwCipherSuites->CipherSuiteCount,
                (UINT32 *)SupportedHwCipherSuites->CipherSuiteList,
                (UINT32 *)CipherSuite
                ))
          {
            *SecurityType = WifiMgrGetSecurityType ((UINT32 *)AKMSuite, (UINT32 *)CipherSuite);

            if (*SecurityType != SECURITY_TYPE_UNKNOWN) {
              if ((AKMSuiteSupported != NULL) && (CipherSuiteSupported != NULL)) {
                *CipherSuiteSupported = TRUE;
              }

              return EFI_SUCCESS;
            }
          }
        }
      }
    }
  }

  *SecurityType = WifiMgrGetSecurityType (
                    (UINT32 *)AKMList->AKMSuiteList,
                    (UINT32 *)CipherList->CipherSuiteList
                    );

  return EFI_SUCCESS;
}

/**
  Get the security type for a certain AKMSuite and CipherSuite.

  @param[in]   AKMSuite             An certain AKMSuite.
  @param[in]   CipherSuite          An certain CipherSuite.

  @return a security type if found, or SECURITY_TYPE_UNKNOWN.

**/
UINT8
WifiMgrGetSecurityType (
  IN  UINT32  *AKMSuite,
  IN  UINT32  *CipherSuite
  )
{
  if ((AKMSuite != NULL) && (*AKMSuite == IEEE_80211_AKM_SUITE_OWE)) {
    return SECURITY_TYPE_NONE;
  }

  if (CipherSuite == NULL) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_NONE;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else if (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_USE_GROUP) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_NONE;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else if ((*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_WEP40) ||
             (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_WEP104))
  {
    return SECURITY_TYPE_WEP;
  } else if (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_CCMP) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_UNKNOWN;
    }

    if (*AKMSuite == IEEE_80211_AKM_SUITE_SAE) {
      return SECURITY_TYPE_WPA3_PERSONAL;
    } else if ((*AKMSuite == IEEE_80211_AKM_SUITE_8021X_OR_PMKSA) ||
               (*AKMSuite == IEEE_80211_AKM_SUITE_8021X_OR_PMKSA_SHA256))
    {
      return SECURITY_TYPE_WPA2_ENTERPRISE;
    } else if ((*AKMSuite == IEEE_80211_AKM_SUITE_PSK) ||
               (*AKMSuite == IEEE_80211_AKM_SUITE_PSK_SHA256))
    {
      return SECURITY_TYPE_WPA2_PERSONAL;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else if (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_TKIP) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_UNKNOWN;
    }

    if ((*AKMSuite == IEEE_80211_AKM_SUITE_8021X_OR_PMKSA) ||
        (*AKMSuite == IEEE_80211_AKM_SUITE_8021X_OR_PMKSA_SHA256))
    {
      return SECURITY_TYPE_WPA_ENTERPRISE;
    } else if ((*AKMSuite == IEEE_80211_AKM_SUITE_PSK) ||
               (*AKMSuite == IEEE_80211_AKM_SUITE_PSK_SHA256))
    {
      return SECURITY_TYPE_WPA_PERSONAL;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else if (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_GCMP) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_UNKNOWN;
    }

    if (*AKMSuite == IEEE_80211_AKM_SUITE_8021X_SUITE_B) {
      return SECURITY_TYPE_WPA3_ENTERPRISE;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else if (*CipherSuite == IEEE_80211_PAIRWISE_CIPHER_SUITE_GCMP256) {
    if (AKMSuite == NULL) {
      return SECURITY_TYPE_UNKNOWN;
    }

    if (*AKMSuite == IEEE_80211_AKM_SUITE_8021X_SUITE_B192) {
      return SECURITY_TYPE_WPA3_ENTERPRISE;
    } else {
      return SECURITY_TYPE_UNKNOWN;
    }
  } else {
    return SECURITY_TYPE_UNKNOWN;
  }
}

/**
  Get supported AKMSuites and CipherSuites from supplicant for a Nic.

  @param[in]   Nic                      The Nic to operate.

  @retval EFI_SUCCESS                   Get the supported suite list successfully.
  @retval EFI_INVALID_PARAMETER         No Nic found or supplicant is NULL.

**/
EFI_STATUS
WifiMgrGetSupportedSuites (
  IN    WIFI_MGR_DEVICE_DATA  *Nic
  )
{
  EFI_STATUS                       Status;
  EFI_SUPPLICANT_PROTOCOL          *Supplicant;
  EFI_80211_AKM_SUITE_SELECTOR     *SupportedAKMSuites;
  EFI_80211_CIPHER_SUITE_SELECTOR  *SupportedSwCipherSuites;
  EFI_80211_CIPHER_SUITE_SELECTOR  *SupportedHwCipherSuites;
  UINTN                            DataSize;

  SupportedAKMSuites      = NULL;
  SupportedSwCipherSuites = NULL;
  SupportedHwCipherSuites = NULL;

  if ((Nic == NULL) || (Nic->Supplicant == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Supplicant = Nic->Supplicant;

  DataSize = 0;
  Status   = Supplicant->GetData (Supplicant, EfiSupplicant80211SupportedAKMSuites, NULL, &DataSize);
  if ((Status == EFI_BUFFER_TOO_SMALL) && (DataSize > 0)) {
    SupportedAKMSuites = AllocateZeroPool (DataSize);
    if (SupportedAKMSuites == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = Supplicant->GetData (
                           Supplicant,
                           EfiSupplicant80211SupportedAKMSuites,
                           (UINT8 *)SupportedAKMSuites,
                           &DataSize
                           );
    if (!EFI_ERROR (Status)) {
      Nic->SupportedSuites.SupportedAKMSuites = SupportedAKMSuites;
    } else {
      FreePool (SupportedAKMSuites);
    }
  } else {
    SupportedAKMSuites = NULL;
  }

  DataSize = 0;
  Status   = Supplicant->GetData (Supplicant, EfiSupplicant80211SupportedSoftwareCipherSuites, NULL, &DataSize);
  if ((Status == EFI_BUFFER_TOO_SMALL) && (DataSize > 0)) {
    SupportedSwCipherSuites = AllocateZeroPool (DataSize);
    if (SupportedSwCipherSuites == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = Supplicant->GetData (
                           Supplicant,
                           EfiSupplicant80211SupportedSoftwareCipherSuites,
                           (UINT8 *)SupportedSwCipherSuites,
                           &DataSize
                           );
    if (!EFI_ERROR (Status)) {
      Nic->SupportedSuites.SupportedSwCipherSuites = SupportedSwCipherSuites;
    } else {
      FreePool (SupportedSwCipherSuites);
    }
  } else {
    SupportedSwCipherSuites = NULL;
  }

  DataSize = 0;
  Status   = Supplicant->GetData (Supplicant, EfiSupplicant80211SupportedHardwareCipherSuites, NULL, &DataSize);
  if ((Status == EFI_BUFFER_TOO_SMALL) && (DataSize > 0)) {
    SupportedHwCipherSuites = AllocateZeroPool (DataSize);
    if (SupportedHwCipherSuites == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = Supplicant->GetData (
                           Supplicant,
                           EfiSupplicant80211SupportedHardwareCipherSuites,
                           (UINT8 *)SupportedHwCipherSuites,
                           &DataSize
                           );
    if (!EFI_ERROR (Status)) {
      Nic->SupportedSuites.SupportedHwCipherSuites = SupportedHwCipherSuites;
    } else {
      FreePool (SupportedHwCipherSuites);
    }
  } else {
    SupportedHwCipherSuites = NULL;
  }

  return EFI_SUCCESS;
}

/**
  Clean secrets from a network profile.

  @param[in]   Profile               The profile to be cleanned.

**/
VOID
WifiMgrCleanProfileSecrets (
  IN  WIFI_MGR_NETWORK_PROFILE  *Profile
  )
{
  ZeroMem (Profile->Password, sizeof (CHAR16) * PASSWORD_STORAGE_SIZE);
  ZeroMem (Profile->EapPassword, sizeof (CHAR16) * PASSWORD_STORAGE_SIZE);
  ZeroMem (Profile->PrivateKeyPassword, sizeof (CHAR16) * PASSWORD_STORAGE_SIZE);

  if (Profile->CACertData != NULL) {
    ZeroMem (Profile->CACertData, Profile->CACertSize);
    FreePool (Profile->CACertData);
  }

  Profile->CACertData = NULL;
  Profile->CACertSize = 0;

  if (Profile->ClientCertData != NULL) {
    ZeroMem (Profile->ClientCertData, Profile->ClientCertSize);
    FreePool (Profile->ClientCertData);
  }

  Profile->ClientCertData = NULL;
  Profile->ClientCertSize = 0;

  if (Profile->PrivateKeyData != NULL) {
    ZeroMem (Profile->PrivateKeyData, Profile->PrivateKeyDataSize);
    FreePool (Profile->PrivateKeyData);
  }

  Profile->PrivateKeyData     = NULL;
  Profile->PrivateKeyDataSize = 0;
}

/**
  Free all network profiles in a profile list.

  @param[in]   ProfileList           The profile list to be freed.

**/
VOID
WifiMgrFreeProfileList (
  IN  LIST_ENTRY  *ProfileList
  )
{
  WIFI_MGR_NETWORK_PROFILE  *Profile;
  LIST_ENTRY                *Entry;
  LIST_ENTRY                *NextEntry;

  if (ProfileList == NULL) {
    return;
  }

  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ProfileList) {
    Profile = NET_LIST_USER_STRUCT_S (
                Entry,
                WIFI_MGR_NETWORK_PROFILE,
                Link,
                WIFI_MGR_PROFILE_SIGNATURE
                );

    WifiMgrCleanProfileSecrets (Profile);

    if (Profile->Network.AKMSuite != NULL) {
      FreePool (Profile->Network.AKMSuite);
    }

    if (Profile->Network.CipherSuite != NULL) {
      FreePool (Profile->Network.CipherSuite);
    }

    FreePool (Profile);
  }
}

/**
  Free user configured hidden network list.

  @param[in]   HiddenList           The hidden network list to be freed.

**/
VOID
WifiMgrFreeHiddenList (
  IN  LIST_ENTRY  *HiddenList
  )
{
  WIFI_HIDDEN_NETWORK_DATA  *HiddenNetwork;
  LIST_ENTRY                *Entry;
  LIST_ENTRY                *NextEntry;

  if (HiddenList == NULL) {
    return;
  }

  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, HiddenList) {
    HiddenNetwork = NET_LIST_USER_STRUCT_S (
                      Entry,
                      WIFI_HIDDEN_NETWORK_DATA,
                      Link,
                      WIFI_MGR_HIDDEN_NETWORK_SIGNATURE
                      );
    FreePool (HiddenNetwork);
  }
}

/**
  Free the resources of a config token.

  @param[in]   ConfigToken          The config token to be freed.
**/
VOID
WifiMgrFreeToken (
  IN   WIFI_MGR_MAC_CONFIG_TOKEN  *ConfigToken
  )
{
  EFI_80211_GET_NETWORKS_RESULT  *Result;

  if (ConfigToken == NULL) {
    return;
  }

  switch (ConfigToken->Type) {
    case TokenTypeGetNetworksToken:

      if (ConfigToken->Token.GetNetworksToken != NULL) {
        gBS->CloseEvent (ConfigToken->Token.GetNetworksToken->Event);
        if (ConfigToken->Token.GetNetworksToken->Data != NULL) {
          FreePool (ConfigToken->Token.GetNetworksToken->Data);
        }

        Result = ConfigToken->Token.GetNetworksToken->Result;
        if (Result != NULL) {
          FreePool (Result);
        }

        FreePool (ConfigToken->Token.GetNetworksToken);
      }

      FreePool (ConfigToken);
      break;

    case TokenTypeConnectNetworkToken:

      if (ConfigToken->Token.ConnectNetworkToken != NULL) {
        gBS->CloseEvent (ConfigToken->Token.ConnectNetworkToken->Event);
        if (ConfigToken->Token.ConnectNetworkToken->Data != NULL) {
          FreePool (ConfigToken->Token.ConnectNetworkToken->Data);
        }

        FreePool (ConfigToken->Token.ConnectNetworkToken);
      }

      FreePool (ConfigToken);
      break;

    case TokenTypeDisconnectNetworkToken:

      if (ConfigToken->Token.DisconnectNetworkToken != NULL) {
        FreePool (ConfigToken->Token.DisconnectNetworkToken);
      }

      FreePool (ConfigToken);
      break;

    default:
      break;
  }
}
