public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "PierreGondois" <pierre.gondois@arm.com>
To: devel@edk2.groups.io
Cc: Sami Mujawar <sami.mujawar@arm.com>,
	Leif Lindholm <quic_llindhol@quicinc.com>,
	Ard Biesheuvel <ardb+tianocore@kernel.org>,
	Rebecca Cran <rebecca@bsdio.com>,
	Michael D Kinney <michael.d.kinney@intel.com>,
	Liming Gao <gaoliming@byosoft.com.cn>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Jian J Wang <jian.j.wang@intel.com>
Subject: [PATCH RESEND v1 6/9] MdePkg/DrbgLib: Add Ctr Drbg mechanism functions
Date: Wed, 29 Jun 2022 21:18:43 +0200	[thread overview]
Message-ID: <20220629191848.2619317-7-Pierre.Gondois@arm.com> (raw)
In-Reply-To: <20220629191848.2619317-1-Pierre.Gondois@arm.com>

From: Pierre Gondois <Pierre.Gondois@arm.com>

>From the NIST Special Publication 800-90A, the implementation
of a Drbg can be split into:
 1. DRBG Mechanism Functions (s9 of the spec), describing the
    operations generic to all the mechanisms.
 2. DRBG Algorithm Specifications (s10 of the spec), describing
    the operations specific to each mechanisms (CTR, HMAC, ...)

This patch implements the 2., i.e. the operations specific to
the Ctr Drbg mechanism.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 MdePkg/Library/DrbgLib/CtrDrbg.c | 899 +++++++++++++++++++++++++++++++
 MdePkg/Library/DrbgLib/CtrDrbg.h | 100 ++++
 2 files changed, 999 insertions(+)
 create mode 100644 MdePkg/Library/DrbgLib/CtrDrbg.c
 create mode 100644 MdePkg/Library/DrbgLib/CtrDrbg.h

diff --git a/MdePkg/Library/DrbgLib/CtrDrbg.c b/MdePkg/Library/DrbgLib/CtrDrbg.c
new file mode 100644
index 000000000000..7db4d724086a
--- /dev/null
+++ b/MdePkg/Library/DrbgLib/CtrDrbg.c
@@ -0,0 +1,899 @@
+/** @file
+  Ctr Drbg implementation.
+  (Counter Deterministic Random Bit Generator)
+  Cf. [1] s10.2.1 CTR_DRBG
+
+  Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - [1] NIST Special Publication 800-90A Revision 1, June 2015, Recommendation
+        for Random Number Generation Using Deterministic Random Bit Generators.
+        (https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final)
+  - [2] NIST Special Publication 800-90B, Recommendation for the Entropy
+        Sources Used for Random Bit Generation.
+        (https://csrc.nist.gov/publications/detail/sp/800-90b/final)
+  - [3] (Second Draft) NIST Special Publication 800-90C, Recommendation for
+        Random Bit Generator (RBG) Constructions.
+        (https://csrc.nist.gov/publications/detail/sp/800-90c/draft)
+  - [4] NIST Special Publication 800-57 Part 1 Revision 5, May 2020,
+        Recommendation for Key Management:Part 1 - General.
+        (https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final)
+  - [5] Unified Extensible Firmware Interface (UEFI) Specification,
+        Version 2.8 Errata B, May 2020
+        (https://www.uefi.org/specifications)
+  - [6] FIPS 197 November 26, 2001:
+        Specification for the ADVANCED ENCRYPTION STANDARD (AES)
+
+  @par Glossary:
+    - TRNG - True Random Number Generator
+    - Sec  - Security
+    - DRBG - Deterministic Random Bits Generator
+    - CTR  - Counter
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+
+#include "Common.h"
+#include "CtrDrbg.h"
+
+/** Get a nonce.
+
+  [1] s10.2.1.3.1 Instantiation When a Derivation Function is Not Used
+
+  When instantiation is performed using this method, full-entropy input
+  is required, and a nonce is not used.
+
+  @param  [in, out] DrbgHandle    The Drbg handle.
+  @param  [out]     NonceStream   Stream containing the Nonce.
+
+  @retval EFI_SUCCESS             Success.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgGetNonce (
+  IN  OUT DRBG_HANDLE  DrbgHandle,
+  OUT     BIT_STREAM   *NonceStream
+  )
+{
+  // Nothing to do.
+  return EFI_SUCCESS;
+}
+
+/** Check the internal state.
+
+  @param  [in] DrbgHandle    The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgCheckInternalState (
+  IN DRBG_HANDLE  DrbgHandle
+  )
+{
+  CTR_INTERNAL_STATE  *CtrIntState;
+
+  CtrIntState = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+
+  // Just check that key and value BitStreams are still ok.
+  if ((IsBitStreamEmpty (CtrIntState->Val)   ||
+       (IsBitStreamEmpty (CtrIntState->Key))))
+  {
+    ASSERT (!IsBitStreamEmpty (CtrIntState->Val));
+    ASSERT (!IsBitStreamEmpty (CtrIntState->Key));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Update algorithm.
+
+  CTR_DRBG_Update implementation.
+
+  Cf. [1] s10.2.1.2 The Update Function (CTR_DRBG_Update)
+
+  @param [in]       ProvidedData  The data to be used. This must be exactly
+                                  seedlen bits in length.
+  @param [in, out]  DrbgHandle    The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgUpdate (
+  IN      BIT_STREAM   *ProvidedData,
+  IN OUT  DRBG_HANDLE  DrbgHandle
+  )
+{
+  EFI_STATUS             Status;
+  BIT_STREAM             *IncStream;
+  BIT_STREAM             *OutBlkStream;
+  BIT_STREAM             *TempStream;
+  CTR_INTERNAL_STATE     *CtrIntState;
+  CTR_VALUE_DEFINITIONS  *CtrVal;
+
+  if (IsBitStreamEmpty (ProvidedData)  ||
+      (DrbgHandle == NULL)            ||
+      (DrbgHandle->IntState.DrbgAlgoIntState == NULL) ||
+      (DrbgHandle->DrbgVal.DrbgAlgoVal == NULL))
+  {
+    ASSERT (!IsBitStreamEmpty (ProvidedData));
+    ASSERT (DrbgHandle != NULL);
+    ASSERT (DrbgHandle->IntState.DrbgAlgoIntState != NULL);
+    ASSERT (DrbgHandle->DrbgVal.DrbgAlgoVal != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  IncStream    = NULL;
+  OutBlkStream = NULL;
+  TempStream   = NULL;
+  CtrIntState  = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+  CtrVal       = (CTR_VALUE_DEFINITIONS *)DrbgHandle->DrbgVal.DrbgAlgoVal;
+
+  if (BitStreamBitLen (ProvidedData) != CtrVal->SeedLen) {
+    ASSERT (BitStreamBitLen (ProvidedData) == CtrVal->SeedLen);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // 1. temp = Null.
+  Status = BitStreamAlloc (0, &TempStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    return Status;
+  }
+
+  //  2. While (len (temp) < seedlen) do
+  while (BitStreamBitLen (TempStream) < CtrVal->SeedLen) {
+    // 2.1 If ctr_len < blocklen
+    if (CtrVal->CtrLen < CtrVal->SeedLen) {
+      // 2.1.1 inc = (rightmost (V, ctr_len) + 1) mod 2 ^ ctr_len.
+      Status = BitStreamRightmost (
+                 CtrIntState->Val,
+                 CtrVal->CtrLen,
+                 &IncStream
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamAddModulo (1, CtrVal->CtrLen, IncStream);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      // 2.1.2 V = leftmost (V, blocklen - ctr_len) || inc.
+      Status = BitStreamLeftmost (
+                 CtrIntState->Val,
+                 CtrVal->BlockLen - CtrVal->CtrLen,
+                 &CtrIntState->Val
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamConcat (
+                 CtrIntState->Val,
+                 IncStream,
+                 &CtrIntState->Val
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamFree (&IncStream);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+    } else {
+      // (2.1) Else V = (V+1) mod 2 ^ blocklen.
+      Status = BitStreamAddModulo (1, CtrVal->BlockLen, CtrIntState->Val);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+    }
+
+    // 2.2 output_block = Block_Encrypt (Key, V).
+    Status = BlockEncrypt (DrbgHandle, &OutBlkStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+
+    // 2.3 temp = temp || output_block.
+    Status = BitStreamConcat (TempStream, OutBlkStream, &TempStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+
+    Status = BitStreamFree (&OutBlkStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  } // while
+
+  // 3. temp = leftmost (temp, seedlen).
+  Status = BitStreamLeftmost (TempStream, CtrVal->SeedLen, &TempStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 4. temp = temp XoR provided_data.
+  Status = BitStreamXor (TempStream, ProvidedData, &TempStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 5. Key = leftmost (temp, keylen).
+  Status = BitStreamFree (&CtrIntState->Key);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  Status = BitStreamLeftmost (TempStream, CtrVal->KeyLen, &CtrIntState->Key);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 6. V = rightmost (temp, blocklen).
+  Status = BitStreamFree (&CtrIntState->Val);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  Status = BitStreamRightmost (
+             TempStream,
+             CtrVal->BlockLen,
+             &CtrIntState->Val
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    // Fall through.
+  }
+
+ExitHandler:
+  if (IncStream != NULL) {
+    BitStreamFree (&IncStream);
+  }
+
+  if (OutBlkStream != NULL) {
+    BitStreamFree (&OutBlkStream);
+  }
+
+  if (TempStream != NULL) {
+    BitStreamFree (&TempStream);
+  }
+
+  // 7. Return (Key, V).
+  return Status;
+}
+
+/** Instantiate algorithm.
+
+  CTR_DRBG_Instantiate_algorithm implementation.
+
+  Cf. [1] s10.2.1.3.1 Instantiation When a Derivation Function is Not Used
+
+  @param  [in]  EntropyInput      The string of bits obtained from the
+                                  randomness source.
+  @param  [in]  PersStrBitStream  The personalization string received from the
+                                  consuming application. Note that the length
+                                  of the personalization_string may be zero.
+                                  Note: PersStrBitStream must be initialized,
+                                  even with a NULL BitStream.
+  @param  [out] DrbgHandle        The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgInstantiateAlgo (
+  IN  BIT_STREAM   *EntropyInput,
+  IN  BIT_STREAM   *PersStrBitStream,
+  OUT DRBG_HANDLE  DrbgHandle
+  )
+{
+  EFI_STATUS             Status;
+  BIT_STREAM             *LocalStream;
+  BIT_STREAM             *SeedMaterial;
+  CTR_INTERNAL_STATE     *CtrIntState;
+  CTR_VALUE_DEFINITIONS  *CtrVal;
+  UINTN                  Temp;
+
+  if (IsBitStreamEmpty (EntropyInput)  ||
+      (PersStrBitStream == NULL)      ||
+      (DrbgHandle == NULL) ||
+      (DrbgHandle->IntState.DrbgAlgoIntState == NULL) ||
+      (DrbgHandle->DrbgVal.DrbgAlgoVal == NULL))
+  {
+    ASSERT (!IsBitStreamEmpty (EntropyInput));
+    ASSERT (PersStrBitStream != NULL);
+    ASSERT (DrbgHandle != NULL);
+    ASSERT (DrbgHandle->IntState.DrbgAlgoIntState != NULL);
+    ASSERT (DrbgHandle->DrbgVal.DrbgAlgoVal != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  LocalStream  = NULL;
+  SeedMaterial = NULL;
+  CtrIntState  = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+  CtrVal       = (CTR_VALUE_DEFINITIONS *)DrbgHandle->DrbgVal.DrbgAlgoVal;
+
+  // 1. temp = len (personalization_string).
+  Temp = BitStreamBitLen (PersStrBitStream);
+
+  // 2. If (temp < seedlen), then
+  // personalization_string = personalization_string || 0 ^ seedlen - temp.
+  if (Temp < CtrVal->SeedLen) {
+    Status = BitStreamAlloc (CtrVal->SeedLen - Temp, &LocalStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      return Status;
+    }
+
+    Status = BitStreamConcat (
+               PersStrBitStream,
+               LocalStream,
+               &PersStrBitStream
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  }
+
+  // 3. seed_material = entropy_input XoR personalization_string.
+  Status = BitStreamXor (EntropyInput, PersStrBitStream, &SeedMaterial);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 4. Key = 0 ^ keylen.
+  Status = BitStreamAlloc (CtrVal->KeyLen, &CtrIntState->Key);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 5. V = 0 ^ blocklen.
+  Status = BitStreamAlloc (CtrVal->BlockLen, &CtrIntState->Val);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 6. (Key, V) = CTR_DRBG_Update (seed_material, Key, V).
+  Status = CtrDrbgUpdate (SeedMaterial, DrbgHandle);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 7. reseed_counter = 1.
+  CtrIntState->ReseedCounter = 1;
+
+ExitHandler:
+  if (SeedMaterial != NULL) {
+    BitStreamFree (&SeedMaterial);
+  }
+
+  if (LocalStream != NULL) {
+    BitStreamFree (&LocalStream);
+  }
+
+  // 8. Return (V, Key, reseed_counter).
+  return Status;
+}
+
+/** Reseed algorithm
+
+  CTR_DRBG_Reseed_algorithm implementation.
+
+  Cf. [1] s10.2.1.4.1 Reseeding When a Derivation Function is Not Used
+
+  @param [in]       EntropyInput  The string of bits obtained from the
+                                  randomness source.
+  @param [in]       AddInput      The additional input string received
+                                  from the consuming application. Note
+                                  that the length of the additional_input
+                                  string may be zero.
+  @param [in, out]  DrbgHandle    The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Out of resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgReseedAlgo (
+  IN      BIT_STREAM   *EntropyInput,
+  IN      BIT_STREAM   *AddInput,
+  IN OUT  DRBG_HANDLE  DrbgHandle
+  )
+{
+  EFI_STATUS             Status;
+  UINTN                  Temp;
+  BIT_STREAM             *LocalStream;
+  BIT_STREAM             *SeedMaterial;
+  CTR_INTERNAL_STATE     *CtrIntState;
+  CTR_VALUE_DEFINITIONS  *CtrVal;
+
+  if (IsBitStreamEmpty (EntropyInput)  ||
+      (AddInput == NULL)              ||
+      (DrbgHandle == NULL) ||
+      (DrbgHandle->IntState.DrbgAlgoIntState == NULL) ||
+      (DrbgHandle->DrbgVal.DrbgAlgoVal == NULL))
+  {
+    ASSERT (!IsBitStreamEmpty (EntropyInput));
+    ASSERT (AddInput != NULL);
+    ASSERT (DrbgHandle != NULL);
+    ASSERT (DrbgHandle->IntState.DrbgAlgoIntState != NULL);
+    ASSERT (DrbgHandle->DrbgVal.DrbgAlgoVal != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  LocalStream  = NULL;
+  SeedMaterial = NULL;
+  CtrIntState  = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+  CtrVal       = (CTR_VALUE_DEFINITIONS *)DrbgHandle->DrbgVal.DrbgAlgoVal;
+
+  // 1. temp = len (additional_input).
+  Temp = BitStreamBitLen (AddInput);
+
+  // 2. If (temp < seedlen),
+  // then additional_input = additional_input || 0 ^ (seedlen - temp).
+  if (Temp < CtrVal->SeedLen) {
+    Status = BitStreamAlloc (CtrVal->SeedLen - Temp, &LocalStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      return Status;
+    }
+
+    Status = BitStreamConcat (
+               AddInput,
+               LocalStream,
+               &AddInput
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  }
+
+  // 3. seed_material = entropy_input XoR additional_input.
+  Status = BitStreamXor (EntropyInput, AddInput, &SeedMaterial);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 4. (Key, V) = CTR_DRBG_Update (seed_material, Key, V).
+  Status = CtrDrbgUpdate (SeedMaterial, DrbgHandle);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 5. reseed_counter = 1.
+  CtrIntState->ReseedCounter = 1;
+
+ExitHandler:
+  if (LocalStream != NULL) {
+    BitStreamFree (&LocalStream);
+  }
+
+  if (SeedMaterial != NULL) {
+    BitStreamFree (&SeedMaterial);
+  }
+
+  // 6. Return (V, Key, reseed_counter).
+  return Status;
+}
+
+/** Generate algorithm.
+
+  CTR_DRBG_Generate_algorithm implementation.
+
+  Cf. s10.2.1.5.1 Generating Pseudorandom Bits When a Derivation Function
+  is Not Used
+
+  To reflect that 'a reseed is required before the requested pseudorandom bits
+  can be generated', the EFI_NOT_READY return code is used.
+
+  @param [in]       AddInput          The additional input string received from
+                                      the consuming application. Note that the
+                                      length of the additional_input string
+                                      may be zero.
+  @param [in]       RequestedNbBits   The number of pseudorandom bits to be
+                                      returned to the generate function.
+  @param [out]      RndBitStream      BitStream containing the random bits.
+  @param [in, out]  DrbgHandle        The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Out of resources.
+  @retval EFI_NOT_READY           A reseed is required before the requested
+                                  pseudorandom bits can be generated.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgGenerateAlgo (
+  IN      BIT_STREAM   *AddInput,
+  IN      UINTN        RequestedNbBits,
+  OUT     BIT_STREAM   **RndBitStream,
+  IN OUT  DRBG_HANDLE  DrbgHandle
+  )
+{
+  EFI_STATUS             Status;
+  UINTN                  Temp;
+  BIT_STREAM             *IncStream;
+  BIT_STREAM             *LocalStream;
+  BIT_STREAM             *OutBlkStream;
+  BIT_STREAM             *TempStream;
+  CTR_INTERNAL_STATE     *CtrIntState;
+  CTR_VALUE_DEFINITIONS  *CtrVal;
+
+  if ((AddInput == NULL)      ||
+      (RequestedNbBits == 0)  ||
+      (RndBitStream == NULL)  ||
+      (*RndBitStream != NULL) ||
+      (DrbgHandle == NULL) ||
+      (DrbgHandle->IntState.DrbgAlgoIntState == NULL) ||
+      (DrbgHandle->DrbgVal.DrbgAlgoVal == NULL))
+  {
+    ASSERT (AddInput != NULL);
+    ASSERT (RequestedNbBits != 0);
+    ASSERT (RndBitStream != NULL);
+    ASSERT (*RndBitStream == NULL);
+    ASSERT (DrbgHandle != NULL);
+    ASSERT (DrbgHandle->IntState.DrbgAlgoIntState != NULL);
+    ASSERT (DrbgHandle->DrbgVal.DrbgAlgoVal != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  IncStream    = NULL;
+  LocalStream  = NULL;
+  OutBlkStream = NULL;
+  TempStream   = NULL;
+  CtrIntState  = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+  CtrVal       = (CTR_VALUE_DEFINITIONS *)DrbgHandle->DrbgVal.DrbgAlgoVal;
+
+  // 1. If reseed_counter > reseed_interval,
+  // then return an indication that a reseed is required.
+  if (CtrIntState->ReseedCounter > CtrVal->ReseedInterval) {
+    return EFI_NOT_READY;
+  }
+
+  // 2. If (additional_input != Null), then
+  if (!IsBitStreamEmpty (AddInput)) {
+    // 2.1 temp = len (additional_input).
+    Temp = BitStreamBitLen (AddInput);
+
+    // 2.2 If (temp < seedlen), then
+    // additional_input = additional_input || 0 seedlen - temp .
+    if (Temp < CtrVal->SeedLen) {
+      Status = BitStreamAlloc (CtrVal->SeedLen - Temp, &LocalStream);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        return Status;
+      }
+
+      Status = BitStreamConcat (AddInput, LocalStream, &AddInput);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+    }
+
+    // 2.3 (Key, V) = CTR_DRBG_Update (additional_input, Key, V).
+    Status = CtrDrbgUpdate (AddInput, DrbgHandle);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  } else {
+    // (2.) Else additional_input = 0 ^ seedlen.
+    Status = BitStreamFree (&AddInput); // Freeing &AddInput, this is wrong, AddInput is now a local parameter
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+
+    Status = BitStreamAlloc (CtrVal->SeedLen, &AddInput);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  }
+
+  // 3. temp = Null.
+  Status = BitStreamAlloc (0, &TempStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 4. While (len (temp) < requested_number_of_bits) do:
+  while (BitStreamBitLen (TempStream) < RequestedNbBits) {
+    // 4.1 If ctr_len < blocklen
+    if (CtrVal->CtrLen < CtrVal->BlockLen) {
+      // 4.1.1 inc = (rightmost (V, ctr_len) + 1) mod 2 ^ ctr_len.
+      Status = BitStreamRightmost (
+                 CtrIntState->Val,
+                 CtrVal->CtrLen,
+                 &IncStream
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamAddModulo (1, CtrVal->CtrLen, IncStream);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      // 4.1.2 V = leftmost (V, blocklen-ctr_len) || inc.
+      Status = BitStreamLeftmost (
+                 CtrIntState->Val,
+                 CtrVal->BlockLen - CtrVal->CtrLen,
+                 &CtrIntState->Val
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamConcat (
+                 CtrIntState->Val,
+                 IncStream,
+                 &CtrIntState->Val
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+
+      Status = BitStreamFree (&IncStream);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+    } else {
+      // (4.1) Else V = (V+1) mod 2 ^ blocklen.
+      Status = BitStreamAddModulo (1, CtrVal->BlockLen, CtrIntState->Val);
+      if (EFI_ERROR (Status)) {
+        ASSERT_EFI_ERROR (Status);
+        goto ExitHandler;
+      }
+    }
+
+    // 4.2 output_block = Block_Encrypt (Key, V).
+    Status = BlockEncrypt (DrbgHandle, &OutBlkStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+
+    // 4.3 temp = temp || output_block.
+    Status = BitStreamConcat (TempStream, OutBlkStream, &TempStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+
+    Status = BitStreamFree (&OutBlkStream);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      goto ExitHandler;
+    }
+  } // while
+
+  // 5. returned_bits = leftmost (temp, requested_number_of_bits).
+  Status = BitStreamLeftmost (TempStream, RequestedNbBits, RndBitStream);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 6. (Key, V) = CTR_DRBG_Update (additional_input, Key, V).
+  Status = CtrDrbgUpdate (AddInput, DrbgHandle);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    goto ExitHandler;
+  }
+
+  // 7. reseed_counter = reseed_counter + 1.
+  CtrIntState->ReseedCounter += 1;
+
+ExitHandler:
+  if (IncStream != NULL) {
+    BitStreamFree (&IncStream);
+  }
+
+  if (LocalStream != NULL) {
+    BitStreamFree (&LocalStream);
+  }
+
+  if (OutBlkStream != NULL) {
+    BitStreamFree (&OutBlkStream);
+  }
+
+  if (TempStream != NULL) {
+    BitStreamFree (&TempStream);
+  }
+
+  // 8. Return (SUCCESS, returned_bits, Key, V, reseed_counter).
+  return Status;
+}
+
+/** Uninstantiate a DRBG instance.
+
+  [1] s9.4 Removing a DRBG Instantiation
+
+  @param  [in, out] DrbgHandle    The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CtrDrbgUninstantiateFn (
+  IN  OUT DRBG_HANDLE  DrbgHandle
+  )
+{
+  EFI_STATUS          Status;
+  EFI_STATUS          ReturnStatus;
+  CTR_INTERNAL_STATE  *CtrIntState;
+
+  if ((DrbgHandle == NULL) ||
+      (DrbgHandle->IntState.DrbgAlgoIntState == NULL) ||
+      (DrbgHandle->DrbgVal.DrbgAlgoVal == NULL))
+  {
+    ASSERT (DrbgHandle != NULL);
+    ASSERT (DrbgHandle->IntState.DrbgAlgoIntState != NULL);
+    ASSERT (DrbgHandle->DrbgVal.DrbgAlgoVal != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ReturnStatus = EFI_SUCCESS;
+  CtrIntState  = (CTR_INTERNAL_STATE *)DrbgHandle->IntState.DrbgAlgoIntState;
+
+  // 1. If state_handle indicates an invalid state, then return (ERROR_FLAG).
+  // 2. Erase the contents of the internal state indicated by state_handle.
+
+  Status = BitStreamFree (&CtrIntState->Key);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    ReturnStatus = Status;
+  }
+
+  Status = BitStreamFree (&CtrIntState->Val);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    ReturnStatus = Status;
+  }
+
+  FreePool (DrbgHandle);
+
+  // 3. Return (SUCCESS).
+  return ReturnStatus;
+}
+
+/** Drbg mechanism specific instantiation steps.
+
+  @param  [in, out] DrbgHandle     The Drbg handle.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    Out of resources.
+**/
+EFI_STATUS
+EFIAPI
+CtrInitHandle (
+  IN  OUT DRBG_HANDLE  DrbgHandle
+  )
+{
+  DRBG_VALUE_DEFINITIONS  *DrbgVal;
+  CTR_INTERNAL_STATE      *CtrIntState;
+  CTR_VALUE_DEFINITIONS   *CtrVal;
+
+  if (DrbgHandle == NULL) {
+    ASSERT (DrbgHandle != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (
+    &DrbgHandle->Algo,
+    &gEfiRngAlgorithmSp80090Ctr256Guid,
+    sizeof (EFI_RNG_ALGORITHM)
+    );
+
+  // Allocate CtrIntState. Fields are init to 0.
+  CtrIntState = AllocateZeroPool (sizeof (CTR_INTERNAL_STATE));
+  if (CtrIntState == NULL) {
+    ASSERT (CtrIntState != NULL);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DrbgHandle->IntState.DrbgAlgoIntState = CtrIntState;
+
+  // Allocate CtrVal. Fields are init to 0.
+  CtrVal = AllocateZeroPool (sizeof (CTR_VALUE_DEFINITIONS));
+  if (CtrVal == NULL) {
+    ASSERT (CtrVal != NULL);
+    FreePool (CtrIntState);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DrbgHandle->DrbgVal.DrbgAlgoVal = CtrVal;
+
+  // CTR DRBG definitions, cf [1] Table 3 'Definitions for the CTR_DRBG'.
+  // No derivation function is used.
+  // Only 256 bits AES is allowed (Cf. [5] 'Section 37.5 Random Number
+  // Generator Protocol')
+
+  // Drbg generic values.
+  DrbgVal                         = &DrbgHandle->DrbgVal;
+  DrbgVal->HighestSuppSecStrength = 256;
+  DrbgVal->MinLen                 = CTR_DRBG_AES_256_SEEDLEN;
+  DrbgVal->MaxLen                 = CTR_DRBG_AES_256_SEEDLEN;
+  DrbgVal->MaxPersStrLen          = CTR_DRBG_AES_256_SEEDLEN;
+  DrbgVal->MaxAddInputLen         = CTR_DRBG_AES_256_SEEDLEN;
+  DrbgVal->MaxNbBitsPerRequest    = 1 << 19;
+
+  // Ctr specific values.
+  CtrVal->BlockLen = CTR_DRBG_AES_BLOCKLEN;
+  // 4 <= ctr_len <= blocklen. Choose blocklen to be faster.
+  CtrVal->CtrLen             = CTR_DRBG_AES_BLOCKLEN;
+  CtrVal->KeyLen             = CTR_DRBG_AES_256_KEYLEN;
+  CtrVal->MinRequiredEntropy = SecStrength256bits;
+  CtrVal->SeedLen            = CTR_DRBG_AES_256_SEEDLEN;
+  CtrVal->ReseedInterval     = (UINT64)1 << 48;
+
+  // CtrDrbg supports both mechanisms.
+  DrbgHandle->PredResSupported = TRUE;
+  DrbgHandle->ReseedSupported  = TRUE;
+
+  // CtrDrbg specific implementations.
+  DrbgHandle->DrbgGetNonce           = CtrDrbgGetNonce;
+  DrbgHandle->DrbgCheckInternalState = CtrDrbgCheckInternalState;
+  DrbgHandle->DrbgUpdate             = CtrDrbgUpdate;
+  DrbgHandle->DrbgReseedAlgo         = CtrDrbgReseedAlgo;
+  DrbgHandle->DrbgGenerateAlgo       = CtrDrbgGenerateAlgo;
+  DrbgHandle->DrbgInstantiateAlgo    = CtrDrbgInstantiateAlgo;
+  DrbgHandle->DrbgUninstantiateFn    = CtrDrbgUninstantiateFn;
+
+  return EFI_SUCCESS;
+}
diff --git a/MdePkg/Library/DrbgLib/CtrDrbg.h b/MdePkg/Library/DrbgLib/CtrDrbg.h
new file mode 100644
index 000000000000..df9c5108388a
--- /dev/null
+++ b/MdePkg/Library/DrbgLib/CtrDrbg.h
@@ -0,0 +1,100 @@
+/** @file
+  Ctr Drbg implementation.
+  (Counter Deterministic Random Bit Generator)
+  Cf. [1] s10.2.1 CTR_DRBG
+
+  Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - [1] NIST Special Publication 800-90A Revision 1, June 2015, Recommendation
+        for Random Number Generation Using Deterministic Random Bit Generators.
+        (https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final)
+  - [2] NIST Special Publication 800-90B, Recommendation for the Entropy
+        Sources Used for Random Bit Generation.
+        (https://csrc.nist.gov/publications/detail/sp/800-90b/final)
+  - [3] (Second Draft) NIST Special Publication 800-90C, Recommendation for
+        Random Bit Generator (RBG) Constructions.
+        (https://csrc.nist.gov/publications/detail/sp/800-90c/draft)
+  - [4] NIST Special Publication 800-57 Part 1 Revision 5, May 2020,
+        Recommendation for Key Management:Part 1 - General.
+        (https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final)
+  - [5] Unified Extensible Firmware Interface (UEFI) Specification,
+        Version 2.8 Errata B, May 2020
+        (https://www.uefi.org/specifications)
+  - [6] FIPS 197 November 26, 2001:
+        Specification for the ADVANCED ENCRYPTION STANDARD (AES)
+
+  @par Glossary:
+    - TRNG - True Random Number Generator
+    - Sec  - Security
+    - DRBG - Deterministic Random Bits Generator
+    - CTR  - Counter
+**/
+
+#ifndef CTR_DRBG_LIB_H_
+#define CTR_DRBG_LIB_H_
+
+/* Blocklen when using CTR DRBG AES-128, AES-192 and
+  AES-256 algorithm.
+
+  Cf. [6] Figure 4. 'Key-Block-Round Combinations'
+  Cf. [1] Table 3 'Definitions for the CTR_DRBG'
+
+  Output block length is also called 'outlen'.
+*/
+#define CTR_DRBG_AES_BLOCKLEN  128
+
+/* Key length when using CTR DRBG AES-256 algorithm.
+
+  Cf. [1] Table 3 'Definitions for the CTR_DRBG'
+  Cf. [5] s37.5 'Random Number Generator Protocol':
+  'Security level must be at least 256 bits'
+*/
+#define CTR_DRBG_AES_256_KEYLEN  256
+
+/** Seed length when using CTR DRBG AES-256 algorithm.
+
+  Cf. [1] Table 3 'Definitions for the CTR_DRBG'
+  Seed length (seedlen = outlen + keylen)
+*/
+#define CTR_DRBG_AES_256_SEEDLEN  (CTR_DRBG_AES_BLOCKLEN +   \
+                                    CTR_DRBG_AES_256_KEYLEN)
+
+/** Ctr specific internal state.
+
+  Some fields defined at [1] s10.2.1.1 'CTR_DRBG Internal State' might be
+  in the DRBG_INTERNAL_STATE structure.
+*/
+typedef struct {
+  // Working state.
+  /// Value
+  BIT_STREAM    *Val;
+  /// Key
+  BIT_STREAM    *Key;
+  /// Counter: Number of requests for pseudorandom bits
+  ///          since instantiation or reseeding
+  UINTN         ReseedCounter;
+} CTR_INTERNAL_STATE;
+
+/** Ctr specific values.
+
+  Cf. [1] Table 3: Definitions for the CTR_DRBG
+  Some fields might be in the DRBG_VALUE_DEFINITIONS structure.
+*/
+typedef struct {
+  /// Input and output block length
+  UINTN     BlockLen;
+  /// Counter field length.
+  UINTN     CtrLen;
+  // Key length.
+  UINTN     KeyLen;
+  /// Required minimum entropy for instantiate and reseed.
+  UINTN     MinRequiredEntropy;
+  /// Seed length.
+  UINTN     SeedLen;
+  /// Maximum number of requests between two reseeds.
+  UINT64    ReseedInterval;
+} CTR_VALUE_DEFINITIONS;
+
+#endif // CTR_DRBG_LIB_H_
-- 
2.25.1


  parent reply	other threads:[~2022-06-29 19:19 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-29 19:18 [PATCH RESEND v1 0/9] Add DrbgLib PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 1/9] MdePkg/DrbgLib: Drbg library interface definition PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 2/9] MdePkg/DrbgLib: Add NULL instance of Drbg Library PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 3/9] MdePkg/DrbgLib: Add BitStream implementation PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 4/9] MdePkg/DrbgLib: Add Get_entropy_input() implementation PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 5/9] MdePkg/DrbgLib: Add common wrappers PierreGondois
2022-06-29 19:18 ` PierreGondois [this message]
2022-06-29 19:18 ` [PATCH RESEND v1 7/9] MdePkg/DrbgLib: Add Drbg mechanism functions and module PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 8/9] ArmVirtPkg: Kvmtool: Add AesLib/DrbgLib for RngDxe PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 9/9] SecurityPkg/RngDxe: Use DrbgLib in RngDxe for Arm PierreGondois
2022-06-29 19:18 ` [PATCH RESEND v1 09/10] SecurityPkg: Update Securitypkg.ci.yaml PierreGondois
2022-06-29 19:18 ` [PATCH v1 10/10] SecurityPkg/RngDxe: Use DrbgLib in RngDxe for Arm PierreGondois
2022-06-30  0:15 ` [edk2-devel] [PATCH RESEND v1 0/9] Add DrbgLib Michael D Kinney
2022-06-30  1:16   ` Yao, Jiewen
2022-07-01  9:49     ` PierreGondois
2022-07-02  6:25       ` Yao, Jiewen
2022-07-04 13:18         ` PierreGondois
2022-07-01  8:30   ` PierreGondois

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220629191848.2619317-7-Pierre.Gondois@arm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox