Home | History | Annotate | Download | only in RngDxe
      1 /** @file
      2   Support routines for RDRAND instruction access.
      3 
      4 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
      5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 #include <Library/RngLib.h>
     16 
     17 #include "RdRand.h"
     18 #include "AesCore.h"
     19 
     20 /**
     21   Calls RDRAND to fill a buffer of arbitrary size with random bytes.
     22 
     23   @param[in]   Length        Size of the buffer, in bytes,  to fill with.
     24   @param[out]  RandBuffer    Pointer to the buffer to store the random result.
     25 
     26   @retval EFI_SUCCESS        Random bytes generation succeeded.
     27   @retval EFI_NOT_READY      Failed to request random bytes.
     28 
     29 **/
     30 EFI_STATUS
     31 EFIAPI
     32 RdRandGetBytes (
     33   IN UINTN         Length,
     34   OUT UINT8        *RandBuffer
     35   )
     36 {
     37   BOOLEAN     IsRandom;
     38   UINT64      TempRand[2];
     39 
     40   while (Length > 0) {
     41     IsRandom = GetRandomNumber128 (TempRand);
     42     if (!IsRandom) {
     43       return EFI_NOT_READY;
     44     }
     45     if (Length >= sizeof (TempRand)) {
     46       WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[0]);
     47       RandBuffer += sizeof (UINT64);
     48       WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[1]);
     49       RandBuffer += sizeof (UINT64);
     50       Length -= sizeof (TempRand);
     51     } else {
     52       CopyMem (RandBuffer, TempRand, Length);
     53       Length = 0;
     54     }
     55   }
     56 
     57   return EFI_SUCCESS;
     58 }
     59 
     60 /**
     61   Creates a 128bit random value that is fully forward and backward prediction resistant,
     62   suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.
     63   This function takes multiple random numbers through RDRAND without intervening
     64   delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the
     65   seed value.
     66 
     67   @param[out]  SeedBuffer    Pointer to a 128bit buffer to store the random seed.
     68 
     69   @retval EFI_SUCCESS        Random seed generation succeeded.
     70   @retval EFI_NOT_READY      Failed to request random bytes.
     71 
     72 **/
     73 EFI_STATUS
     74 EFIAPI
     75 RdRandGetSeed128 (
     76   OUT UINT8        *SeedBuffer
     77   )
     78 {
     79   EFI_STATUS  Status;
     80   UINT8       RandByte[16];
     81   UINT8       Key[16];
     82   UINT8       Ffv[16];
     83   UINT8       Xored[16];
     84   UINT32      Index;
     85   UINT32      Index2;
     86 
     87   //
     88   // Chose an arbitary key and zero the feed_forward_value (FFV)
     89   //
     90   for (Index = 0; Index < 16; Index++) {
     91     Key[Index] = (UINT8) Index;
     92     Ffv[Index] = 0;
     93   }
     94 
     95   //
     96   // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value
     97   // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.
     98   //
     99   for (Index = 0; Index < 32; Index++) {
    100     MicroSecondDelay (10);
    101     Status = RdRandGetBytes (16, RandByte);
    102     if (EFI_ERROR (Status)) {
    103       return Status;
    104     }
    105 
    106     //
    107     // Perform XOR operations on two 128-bit value.
    108     //
    109     for (Index2 = 0; Index2 < 16; Index2++) {
    110       Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];
    111     }
    112 
    113     AesEncrypt (Key, Xored, Ffv);
    114   }
    115 
    116   for (Index = 0; Index < 16; Index++) {
    117     SeedBuffer[Index] = Ffv[Index];
    118   }
    119 
    120   return EFI_SUCCESS;
    121 }
    122 
    123 /**
    124   Generate high-quality entropy source through RDRAND.
    125 
    126   @param[in]   Length        Size of the buffer, in bytes, to fill with.
    127   @param[out]  Entropy       Pointer to the buffer to store the entropy data.
    128 
    129   @retval EFI_SUCCESS        Entropy generation succeeded.
    130   @retval EFI_NOT_READY      Failed to request random data.
    131 
    132 **/
    133 EFI_STATUS
    134 EFIAPI
    135 RdRandGenerateEntropy (
    136   IN UINTN         Length,
    137   OUT UINT8        *Entropy
    138   )
    139 {
    140   EFI_STATUS  Status;
    141   UINTN       BlockCount;
    142   UINT8       Seed[16];
    143   UINT8       *Ptr;
    144 
    145   Status     = EFI_NOT_READY;
    146   BlockCount = Length / 16;
    147   Ptr        = (UINT8 *)Entropy;
    148 
    149   //
    150   // Generate high-quality seed for DRBG Entropy
    151   //
    152   while (BlockCount > 0) {
    153     Status = RdRandGetSeed128 (Seed);
    154     if (EFI_ERROR (Status)) {
    155       return Status;
    156     }
    157     CopyMem (Ptr, Seed, 16);
    158 
    159     BlockCount--;
    160     Ptr = Ptr + 16;
    161   }
    162 
    163   //
    164   // Populate the remained data as request.
    165   //
    166   Status = RdRandGetSeed128 (Seed);
    167   if (EFI_ERROR (Status)) {
    168     return Status;
    169   }
    170   CopyMem (Ptr, Seed, (Length % 16));
    171 
    172   return Status;
    173 }
    174