]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
Add UEFI RNG Protocol support. The driver will leverage Intel Secure Key technology...
[mirror_edk2.git] / SecurityPkg / RandomNumberGenerator / RngDxe / RdRand.c
diff --git a/SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c b/SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
new file mode 100644 (file)
index 0000000..7e618dc
--- /dev/null
@@ -0,0 +1,395 @@
+/** @file\r
+  Support routines for RDRAND instruction access.\r
+\r
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "RdRand.h"\r
+#include "AesCore.h"\r
+\r
+//\r
+// Bit mask used to determine if RdRand instruction is supported.\r
+//\r
+#define RDRAND_MASK    0x40000000\r
+\r
+/**\r
+  Determines whether or not RDRAND instruction is supported by the host hardware.\r
+\r
+  @retval EFI_SUCCESS          RDRAND instruction supported.\r
+  @retval EFI_UNSUPPORTED      RDRAND instruction not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IsRdRandSupported (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      RegEax;\r
+  UINT32      RegEbx;\r
+  UINT32      RegEcx;\r
+  UINT32      RegEdx;\r
+  BOOLEAN     IsIntelCpu;\r
+\r
+  Status     = EFI_UNSUPPORTED;\r
+  IsIntelCpu = FALSE;\r
+  \r
+  //\r
+  // Checks whether the current processor is an Intel product by CPUID.\r
+  //\r
+  AsmCpuid (0, &RegEax, &RegEbx, &RegEcx, &RegEdx);\r
+  if ((CompareMem ((CHAR8 *)(&RegEbx), "Genu", 4) == 0) &&\r
+      (CompareMem ((CHAR8 *)(&RegEdx), "ineI", 4) == 0) &&\r
+      (CompareMem ((CHAR8 *)(&RegEcx), "ntel", 4) == 0)) {\r
+    IsIntelCpu = TRUE;\r
+  }\r
+\r
+  if (IsIntelCpu) {\r
+    //\r
+    // Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.\r
+    // A value of 1 indicates that processor supports RDRAND instruction.\r
+    //\r
+    AsmCpuid (1, 0, 0, &RegEcx, 0);\r
+\r
+    if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Calls RDRAND to obtain a 16-bit random number.\r
+\r
+  @param[out]  Rand          Buffer pointer to store the random result.\r
+  @param[in]   NeedRetry     Determine whether or not to loop retry.\r
+\r
+  @retval EFI_SUCCESS        RDRAND call was successful.\r
+  @retval EFI_NOT_READY      Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand16 (\r
+  OUT UINT16       *Rand,\r
+  IN BOOLEAN       NeedRetry\r
+  )\r
+{\r
+  UINT32      Index;\r
+  UINT32      RetryCount;\r
+\r
+  if (NeedRetry) {\r
+    RetryCount = RETRY_LIMIT;\r
+  } else {\r
+    RetryCount = 1;\r
+  }\r
+\r
+  //\r
+  // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+  //\r
+  for (Index = 0; Index < RetryCount; Index++) {\r
+    if (RdRand16Step (Rand)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+  \r
+  return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+  Calls RDRAND to obtain a 32-bit random number.\r
+\r
+  @param[out]  Rand          Buffer pointer to store the random result.\r
+  @param[in]   NeedRetry     Determine whether or not to loop retry.\r
+\r
+  @retval EFI_SUCCESS        RDRAND call was successful.\r
+  @retval EFI_NOT_READY      Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand32 (\r
+  OUT UINT32       *Rand,\r
+  IN BOOLEAN       NeedRetry\r
+  )\r
+{\r
+  UINT32      Index;\r
+  UINT32      RetryCount;\r
+\r
+  if (NeedRetry) {\r
+    RetryCount = RETRY_LIMIT;\r
+  } else {\r
+    RetryCount = 1;\r
+  }\r
+\r
+  //\r
+  // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+  //\r
+  for (Index = 0; Index < RetryCount; Index++) {\r
+    if (RdRand32Step (Rand)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+  \r
+  return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+  Calls RDRAND to obtain a 64-bit random number.\r
+\r
+  @param[out]  Rand          Buffer pointer to store the random result.\r
+  @param[in]   NeedRetry     Determine whether or not to loop retry.\r
+\r
+  @retval EFI_SUCCESS        RDRAND call was successful.\r
+  @retval EFI_NOT_READY      Failed attempts to call RDRAND.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRand64 (\r
+  OUT UINT64       *Rand,\r
+  IN BOOLEAN       NeedRetry\r
+  )\r
+{\r
+  UINT32      Index;\r
+  UINT32      RetryCount;\r
+\r
+  if (NeedRetry) {\r
+    RetryCount = RETRY_LIMIT;\r
+  } else {\r
+    RetryCount = 1;\r
+  }\r
+\r
+  //\r
+  // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
+  //\r
+  for (Index = 0; Index < RetryCount; Index++) {\r
+    if (RdRand64Step (Rand)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+  \r
+  return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+  Calls RDRAND to fill a buffer of arbitrary size with random bytes.\r
+\r
+  @param[in]   Length        Size of the buffer, in bytes,  to fill with.\r
+  @param[out]  RandBuffer    Pointer to the buffer to store the random result.\r
+\r
+  @retval EFI_SUCCESS        Random bytes generation succeeded.\r
+  @retval EFI_NOT_READY      Failed to request random bytes.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGetBytes (\r
+  IN UINTN         Length,\r
+  OUT UINT8        *RandBuffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       *Start;\r
+  UINT8       *ResidualStart;\r
+  UINTN       *BlockStart;\r
+  UINTN       TempRand;\r
+  UINTN       Count;\r
+  UINTN       Residual;\r
+  UINTN       StartLen;\r
+  UINTN       BlockNum;\r
+  UINTN       Index;\r
+\r
+  ResidualStart = NULL;\r
+  TempRand      = 0;\r
+\r
+  //\r
+  // Compute the address of the first word aligned (32/64-bit) block in the \r
+  // destination buffer, depending on whether we are in 32- or 64-bit mode.\r
+  //\r
+  Start = RandBuffer;\r
+  if (((UINT32)(UINTN)Start % (UINT32)sizeof(UINTN)) == 0) {\r
+    BlockStart = (UINTN *)Start;\r
+    Count      = Length;\r
+    StartLen   = 0;\r
+  } else {\r
+    BlockStart = (UINTN *)(((UINTN)Start & ~(UINTN)(sizeof(UINTN) - 1)) + (UINTN)sizeof(UINTN));\r
+    Count      = Length - (sizeof (UINTN) - (UINT32)((UINTN)Start % sizeof (UINTN)));\r
+    StartLen   = (UINT32)((UINTN)BlockStart - (UINTN)Start);\r
+  }\r
+\r
+  //\r
+  // Compute the number of word blocks and the remaining number of bytes.\r
+  //\r
+  Residual = Count % sizeof (UINTN);\r
+  BlockNum = Count / sizeof (UINTN);\r
+  if (Residual != 0) {\r
+    ResidualStart = (UINT8 *) (BlockStart + BlockNum);\r
+  }\r
+\r
+  //\r
+  // Obtain a temporary random number for use in the residuals. Failout if retry fails.\r
+  //\r
+  if (StartLen > 0) {\r
+    Status = RdRandWord ((UINTN *) &TempRand, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Populate the starting mis-aligned block.\r
+  //\r
+  for (Index = 0; Index < StartLen; Index++) {\r
+    Start[Index] = (UINT8)(TempRand & 0xff);\r
+    TempRand     = TempRand >> 8;\r
+  }\r
+\r
+  //\r
+  // Populate the central aligned block. Fail out if retry fails.\r
+  //\r
+  Status = RdRandGetWords (BlockNum, (UINTN *)(BlockStart));\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Populate the final mis-aligned block.\r
+  //\r
+  if (Residual > 0) {\r
+    Status = RdRandWord ((UINTN *)&TempRand, TRUE);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    for (Index = 0; Index < Residual; Index++) {\r
+      ResidualStart[Index] = (UINT8)(TempRand & 0xff);\r
+      TempRand             = TempRand >> 8;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Creates a 128bit random value that is fully forward and backward prediction resistant,\r
+  suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.\r
+  This function takes multiple random numbers through RDRAND without intervening\r
+  delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the\r
+  seed value.\r
+  \r
+  @param[out]  SeedBuffer    Pointer to a 128bit buffer to store the random seed.\r
+\r
+  @retval EFI_SUCCESS        Random seed generation succeeded.\r
+  @retval EFI_NOT_READY      Failed to request random bytes.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGetSeed128 (\r
+  OUT UINT8        *SeedBuffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       RandByte[16];\r
+  UINT8       Key[16];\r
+  UINT8       Ffv[16];\r
+  UINT8       Xored[16];\r
+  UINT32      Index;\r
+  UINT32      Index2;\r
+\r
+  //\r
+  // Chose an arbitary key and zero the feed_forward_value (FFV)\r
+  //\r
+  for (Index = 0; Index < 16; Index++) {\r
+    Key[Index] = (UINT8) Index;\r
+    Ffv[Index] = 0;\r
+  }\r
+\r
+  //\r
+  // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value\r
+  // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.\r
+  //\r
+  for (Index = 0; Index < 32; Index++) {\r
+    MicroSecondDelay (10);\r
+    Status = RdRandGetBytes (16, RandByte);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Perform XOR operations on two 128-bit value.\r
+    //\r
+    for (Index2 = 0; Index2 < 16; Index2++) {\r
+      Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];\r
+    }\r
+\r
+    AesEncrypt (Key, Xored, Ffv);\r
+  }\r
+\r
+  for (Index = 0; Index < 16; Index++) {\r
+    SeedBuffer[Index] = Ffv[Index];\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Generate high-quality entropy source through RDRAND.\r
+\r
+  @param[in]   Length        Size of the buffer, in bytes, to fill with.\r
+  @param[out]  Entropy       Pointer to the buffer to store the entropy data.\r
+\r
+  @retval EFI_SUCCESS        Entropy generation succeeded.\r
+  @retval EFI_NOT_READY      Failed to request random data.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RdRandGenerateEntropy (\r
+  IN UINTN         Length,\r
+  OUT UINT8        *Entropy\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINTN       BlockCount;\r
+  UINT8       Seed[16];\r
+  UINT8       *Ptr;\r
+\r
+  Status     = EFI_NOT_READY;\r
+  BlockCount = Length / 16;\r
+  Ptr        = (UINT8 *)Entropy;\r
+\r
+  //\r
+  // Generate high-quality seed for DRBG Entropy\r
+  //\r
+  while (BlockCount > 0) {\r
+    Status = RdRandGetSeed128 (Seed);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    CopyMem (Ptr, Seed, 16);\r
+\r
+    BlockCount--;\r
+    Ptr = Ptr + 16;\r
+  }\r
+\r
+  //\r
+  // Populate the remained data as request.\r
+  //\r
+  Status = RdRandGetSeed128 (Seed);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  CopyMem (Ptr, Seed, (Length % 16));\r
+\r
+  return Status;\r
+}\r