/** @file
  Application for Cryptographic Primitives Validation.

Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/DebugLib.h>
#include <Library/BaseCryptLib.h>

#define  RSA_MODULUS_LENGTH  1024

//
// Public Modulus of RSA Key
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 RsaN[] = {
    0xd3,0x88,0x4c,0x6e,0xf0,0x5e,0x97,0xfd,0x55,0x8f,0xa8,0xf9,0xb3,0x05,
    0xcd,0xe6,0xa9,0x47,0xcc,0xbc,0xe0,0x74,0xc6,0xd6,0x93,0x64,0x04,0x1b,0x49,
    0xfc,0xad,0xfa,0xea,0xb1,0xbb,0xb1,0x8d,0x69,0x2b,0x46,0xa5,0xd2,0x6f,0x0a,
    0xe3,0x85,0x2f,0xbf,0x85,0x15,0x74,0x85,0x2c,0x97,0xe6,0x66,0xc8,0x98,0x45,
    0xa8,0x8e,0x4a,0x93,0xc3,0x0e,0xe4,0x0b,0x06,0x41,0xb4,0x9b,0xf0,0x01,0x93,
    0xca,0x4b,0xb6,0x86,0xc4,0xcb,0xa5,0x20,0x24,0xfa,0x52,0xe8,0xe8,0xa4,0xbb,
    0x5e,0xb5,0xf3,0xeb,0xb0,0x60,0xa7,0x19,0xa4,0xf8,0xf6,0x16,0xdb,0xb9,0x77,
    0xe1,0x45,0x32,0x24,0xb6,0xdd,0x20,0x88,0xf3,0x38,0x4b,0x70,0x5d,0xdf,0x33,
    0x62,0x7b,0xfc,0x85,0xee,0x74,0x23,0x84,0xad
  };

//
// Public Exponent of RSA Key

GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 RsaE[] = { 0x01, 0x00,0x01 };

//
// Private Exponent of RSA Key
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 RsaD[] = {
    0x05,0xb8,0x2d,0xb8,0xe2,0x2e,0xec,0x95,0x20,0xf7,0x1d,0x6e,0x82,0xf4,0xa9,
    0x38,0x8f,0x63,0x2d,0x99,0xfd,0xc7,0xaa,0x88,0xaa,0xbb,0x8d,0x90,0xdf,0x53,
    0x41,0xb1,0x03,0x1f,0x83,0x1f,0xad,0xdf,0x26,0x18,0x66,0xeb,0x65,0xad,0x39,
    0xb1,0xa4,0x34,0xdd,0x78,0x53,0x0a,0x36,0xc6,0x74,0xa8,0x5a,0xcc,0x23,0x36,
    0x07,0x82,0xe4,0xb3,0xd8,0xdb,0xd2,0x8b,0xc0,0xd8,0x25,0x2f,0xb9,0x7f,0xac,
    0xc5,0x41,0xd7,0x76,0x01,0x05,0x7a,0x41,0x2f,0xfd,0x55,0x48,0xcd,0xe9,0xb7,
    0xaa,0xdd,0x05,0x90,0xc5,0x7e,0x95,0x25,0xfb,0xe8,0x5f,0xc2,0x91,0x5e,0x17,
    0x5c,0xe3,0xb8,0x1f,0x5a,0xd8,0x8d,0xae,0xc1,0x39,0xb9,0x7f,0x8c,0x53,0x23,
    0x4e,0x94,0x60,0x3e,0x1d,0x20,0xe0,0x01
  };

//
// Known Answer Test (KAT) Data for RSA PKCS#1 Signing
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 RsaSignData[] = "This message from lab-z.com";

//
// Known Signature for the above message, under SHA-1 Digest
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT8 RsaPkcs1Signature[] = {
0x61,0xA2,0xDD,0x93,0x1A,0xA6,0x1B,0x46,0x2A,0x84,0xC8,0x7A,0x74,0xBB,0x23,0x44,
0x43,0xD6,0xE9,0x2A,0x30,0xAF,0x2D,0x13,0x0A,0x74,0x26,0x78,0xF6,0x42,0x23,0x4D,
0x55,0x85,0xC9,0x42,0xA8,0x4A,0xAC,0x2C,0x46,0x76,0xF5,0x34,0xC7,0x57,0x3B,0x2F,
0x4B,0xF9,0x42,0x03,0x1F,0x80,0xCE,0xF2,0xD7,0xA4,0x8C,0xBB,0xBF,0x37,0x60,0x4A,
0x32,0x3A,0xF2,0x82,0x95,0xF3,0x11,0x40,0x2E,0x45,0x4B,0x2E,0x02,0xBA,0xAA,0xFC,
0x29,0x8D,0xEC,0x56,0xC6,0xCD,0x97,0x06,0xDE,0x52,0x85,0xDB,0x1B,0x17,0xF1,0x39,
0xBB,0x6B,0x8C,0xAA,0xFE,0xEC,0xD4,0xA7,0x96,0x6F,0x22,0xCD,0x4B,0x6D,0x01,0x0B,
0x00,0xA1,0xDF,0x7F,0xA4,0xA0,0xD2,0xC4,0x09,0x0C,0xB0,0x4A,0x7A,0xA2,0xE2,0x93
};

/**
  Entry Point of Cryptographic Validation Utility.

  @param  ImageHandle  The image handle of the UEFI Application.
  @param  SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
CryptestMain (
  IN     EFI_HANDLE                 ImageHandle,
  IN     EFI_SYSTEM_TABLE           *SystemTable
  )
{
  VOID     *Rsa;
  UINT8    HashValue[SHA1_DIGEST_SIZE];
  UINTN    HashSize;
  UINTN    CtxSize;
  VOID     *Sha1Ctx;
  UINT8    *Signature;
  UINTN    SigSize;
  BOOLEAN  Status;
  UINTN i;
  UINT8    sign[SHA1_DIGEST_SIZE];

  RandomSeed (NULL, 0);

  //
  // SHA-1 Digest Message for PKCS#1 Signature 
  //
  Print (L"Hash Original Message ...\n");
  HashSize = SHA1_DIGEST_SIZE;
  ZeroMem (HashValue, HashSize);
  CtxSize = Sha1GetContextSize ();
  Sha1Ctx = AllocatePool (CtxSize);

  Status  = Sha1Init (Sha1Ctx);
  if (!Status) {
    Print (L"[Fail]");
    return EFI_ABORTED;
  }

  Status  = Sha1Update (Sha1Ctx, RsaSignData, AsciiStrLen (RsaSignData));
  if (!Status) {
    Print (L"[Fail]");
    return EFI_ABORTED;
  }

  Status  = Sha1Final (Sha1Ctx, HashValue);
  if (!Status) {
    Print (L"[Fail]");
    return EFI_ABORTED;
  }
  
  for (i=0;i<HashSize;i++) {Print (L"%02X  ",HashValue[i]);}
  
  FreePool (Sha1Ctx);

  //
  // Sign RSA PKCS#1-encoded Signature
  //
  Print (L"PKCS#1 Signature ...\n");

  Rsa = RsaNew ();
  if (Rsa == NULL) {
    Print (L"[Fail]t\n");
    return EFI_ABORTED;
  }

  Status = RsaSetKey (Rsa, RsaKeyN, RsaN, sizeof (RsaN));
  if (!Status) {
    Print (L"[Fail]p\n");
    return EFI_ABORTED;
  }

  Status = RsaSetKey (Rsa, RsaKeyE, RsaE, sizeof (RsaE));
  if (!Status) {
    Print (L"[Fail]h\n");
    return EFI_ABORTED;
  }
  //Private Key
  Status = RsaSetKey (Rsa, RsaKeyD, RsaD, sizeof (RsaD));
  if (!Status) {
    Print (L"[Fail]q\n");
    return EFI_ABORTED;
  }

  SigSize = 0;
  Status  = RsaPkcs1Sign (Rsa, HashValue, HashSize, sign, &SigSize);
  if (Status || SigSize == 0) {
    return EFI_ABORTED;
  }
  Signature = AllocatePool (SigSize);
  Status  = RsaPkcs1Sign (Rsa, HashValue, HashSize, Signature, &SigSize);
  if (!Status) {
    Print (L"[Fail]y\n");
    return EFI_ABORTED;
  }

  for (i=0;i<SigSize;i++) { Print(L"%02X  ",Signature[i]);} Print(L" \n");
  
  if (SigSize != sizeof (RsaPkcs1Signature)) {
    Print (L"[Fail]\n");
    return EFI_ABORTED;
  }

  if (CompareMem (Signature, RsaPkcs1Signature, SigSize) != 0) {
    Print (L"[Fail]a2\n");
    return EFI_ABORTED;
  }

  //
  // Release Resources
  //
  RsaFree (Rsa);
  Print (L"Release RSA Context ... [Pass]");

  Print (L"\n");

  return EFI_SUCCESS;
}
