--- /dev/null
+/** @file\r
+ ParallelHash Implementation.\r
+\r
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "CryptParallelHash.h"\r
+#include <Library/MmServicesTableLib.h>\r
+#include <Library/SynchronizationLib.h>\r
+\r
+#define PARALLELHASH_CUSTOMIZATION "ParallelHash"\r
+\r
+UINTN mBlockNum;\r
+UINTN mBlockSize;\r
+UINTN mLastBlockSize;\r
+UINT8 *mInput;\r
+UINTN mBlockResultSize;\r
+UINT8 *mBlockHashResult;\r
+BOOLEAN *mBlockIsCompleted;\r
+SPIN_LOCK *mSpinLockList;\r
+\r
+/**\r
+ Complete computation of digest of each block.\r
+\r
+ Each AP perform the function called by BSP.\r
+\r
+ @param[in] ProcedureArgument Argument of the procedure.\r
+**/\r
+VOID\r
+EFIAPI\r
+ParallelHashApExecute (\r
+ IN VOID *ProcedureArgument\r
+ )\r
+{\r
+ UINTN Index;\r
+ BOOLEAN Status;\r
+\r
+ for (Index = 0; Index < mBlockNum; Index++) {\r
+ if (AcquireSpinLockOrFail (&mSpinLockList[Index])) {\r
+ //\r
+ // Completed, try next one.\r
+ //\r
+ if (mBlockIsCompleted[Index]) {\r
+ ReleaseSpinLock (&mSpinLockList[Index]);\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Calculate CShake256 for this block.\r
+ //\r
+ Status = CShake256HashAll (\r
+ mInput + Index * mBlockSize,\r
+ (Index == (mBlockNum - 1)) ? mLastBlockSize : mBlockSize,\r
+ mBlockResultSize,\r
+ NULL,\r
+ 0,\r
+ NULL,\r
+ 0,\r
+ mBlockHashResult + Index * mBlockResultSize\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ mBlockIsCompleted[Index] = TRUE;\r
+ }\r
+\r
+ ReleaseSpinLock (&mSpinLockList[Index]);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Dispatch the block task to each AP in SMM mode.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+MmDispatchBlockToAP (\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < gMmst->NumberOfCpus; Index++) {\r
+ if (Index != gMmst->CurrentlyExecutingCpu) {\r
+ gMmst->MmStartupThisAp (ParallelHashApExecute, Index, NULL);\r
+ }\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+/**\r
+ Parallel hash function ParallelHash256, as defined in NIST's Special Publication 800-185,\r
+ published December 2016.\r
+\r
+ @param[in] Input Pointer to the input message (X).\r
+ @param[in] InputByteLen The number(>0) of input bytes provided for the input data.\r
+ @param[in] BlockSize The size of each block (B).\r
+ @param[out] Output Pointer to the output buffer.\r
+ @param[in] OutputByteLen The desired number of output bytes (L).\r
+ @param[in] Customization Pointer to the customization string (S).\r
+ @param[in] CustomByteLen The length of the customization string in bytes.\r
+\r
+ @retval TRUE ParallelHash256 digest computation succeeded.\r
+ @retval FALSE ParallelHash256 digest computation failed.\r
+ @retval FALSE This interface is not supported.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ParallelHash256HashAll (\r
+ IN CONST VOID *Input,\r
+ IN UINTN InputByteLen,\r
+ IN UINTN BlockSize,\r
+ OUT VOID *Output,\r
+ IN UINTN OutputByteLen,\r
+ IN CONST VOID *Customization,\r
+ IN UINTN CustomByteLen\r
+ )\r
+{\r
+ UINT8 EncBufB[sizeof (UINTN)+1];\r
+ UINTN EncSizeB;\r
+ UINT8 EncBufN[sizeof (UINTN)+1];\r
+ UINTN EncSizeN;\r
+ UINT8 EncBufL[sizeof (UINTN)+1];\r
+ UINTN EncSizeL;\r
+ UINTN Index;\r
+ UINT8 *CombinedInput;\r
+ UINTN CombinedInputSize;\r
+ BOOLEAN AllCompleted;\r
+ UINTN Offset;\r
+ BOOLEAN ReturnValue;\r
+\r
+ if ((InputByteLen == 0) || (OutputByteLen == 0) || (BlockSize == 0)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((Input == NULL) || (Output == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((CustomByteLen != 0) && (Customization == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ mBlockSize = BlockSize;\r
+\r
+ //\r
+ // Calculate block number n.\r
+ //\r
+ mBlockNum = InputByteLen % mBlockSize == 0 ? InputByteLen / mBlockSize : InputByteLen / mBlockSize + 1;\r
+\r
+ //\r
+ // Set hash result size of each block in bytes.\r
+ //\r
+ mBlockResultSize = OutputByteLen;\r
+\r
+ //\r
+ // Encode B, n, L to string and record size.\r
+ //\r
+ EncSizeB = LeftEncode (EncBufB, mBlockSize);\r
+ EncSizeN = RightEncode (EncBufN, mBlockNum);\r
+ EncSizeL = RightEncode (EncBufL, OutputByteLen * CHAR_BIT);\r
+\r
+ //\r
+ // Allocate buffer for combined input (newX), Block completed flag and SpinLock.\r
+ //\r
+ CombinedInputSize = EncSizeB + EncSizeN + EncSizeL + mBlockNum * mBlockResultSize;\r
+ CombinedInput = AllocateZeroPool (CombinedInputSize);\r
+ mBlockIsCompleted = AllocateZeroPool (mBlockNum * sizeof (BOOLEAN));\r
+ mSpinLockList = AllocatePool (mBlockNum * sizeof (SPIN_LOCK));\r
+ if ((CombinedInput == NULL) || (mBlockIsCompleted == NULL) || (mSpinLockList == NULL)) {\r
+ ReturnValue = FALSE;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Fill LeftEncode(B).\r
+ //\r
+ CopyMem (CombinedInput, EncBufB, EncSizeB);\r
+\r
+ //\r
+ // Prepare for parallel hash.\r
+ //\r
+ mBlockHashResult = CombinedInput + EncSizeB;\r
+ mInput = (UINT8 *)Input;\r
+ mLastBlockSize = InputByteLen % mBlockSize == 0 ? mBlockSize : InputByteLen % mBlockSize;\r
+\r
+ //\r
+ // Initialize SpinLock for each result block.\r
+ //\r
+ for (Index = 0; Index < mBlockNum; Index++) {\r
+ InitializeSpinLock (&mSpinLockList[Index]);\r
+ }\r
+\r
+ //\r
+ // Dispatch blocklist to each AP.\r
+ //\r
+ if (gMmst != NULL) {\r
+ MmDispatchBlockToAP ();\r
+ }\r
+\r
+ //\r
+ // Wait until all block hash completed.\r
+ //\r
+ do {\r
+ AllCompleted = TRUE;\r
+ for (Index = 0; Index < mBlockNum; Index++) {\r
+ if (AcquireSpinLockOrFail (&mSpinLockList[Index])) {\r
+ if (!mBlockIsCompleted[Index]) {\r
+ AllCompleted = FALSE;\r
+ ReturnValue = CShake256HashAll (\r
+ mInput + Index * mBlockSize,\r
+ (Index == (mBlockNum - 1)) ? mLastBlockSize : mBlockSize,\r
+ mBlockResultSize,\r
+ NULL,\r
+ 0,\r
+ NULL,\r
+ 0,\r
+ mBlockHashResult + Index * mBlockResultSize\r
+ );\r
+ if (ReturnValue) {\r
+ mBlockIsCompleted[Index] = TRUE;\r
+ }\r
+\r
+ ReleaseSpinLock (&mSpinLockList[Index]);\r
+ break;\r
+ }\r
+\r
+ ReleaseSpinLock (&mSpinLockList[Index]);\r
+ } else {\r
+ AllCompleted = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ } while (!AllCompleted);\r
+\r
+ //\r
+ // Fill LeftEncode(n).\r
+ //\r
+ Offset = EncSizeB + mBlockNum * mBlockResultSize;\r
+ CopyMem (CombinedInput + Offset, EncBufN, EncSizeN);\r
+\r
+ //\r
+ // Fill LeftEncode(L).\r
+ //\r
+ Offset += EncSizeN;\r
+ CopyMem (CombinedInput + Offset, EncBufL, EncSizeL);\r
+\r
+ ReturnValue = CShake256HashAll (\r
+ CombinedInput,\r
+ CombinedInputSize,\r
+ OutputByteLen,\r
+ PARALLELHASH_CUSTOMIZATION,\r
+ AsciiStrLen (PARALLELHASH_CUSTOMIZATION),\r
+ Customization,\r
+ CustomByteLen,\r
+ Output\r
+ );\r
+\r
+Exit:\r
+ ZeroMem (CombinedInput, CombinedInputSize);\r
+\r
+ if (CombinedInput != NULL) {\r
+ FreePool (CombinedInput);\r
+ }\r
+\r
+ if (mSpinLockList != NULL) {\r
+ FreePool ((VOID *)mSpinLockList);\r
+ }\r
+\r
+ if (mBlockIsCompleted != NULL) {\r
+ FreePool (mBlockIsCompleted);\r
+ }\r
+\r
+ return ReturnValue;\r
+}\r