/**
 * Configure the RSA and SHA for the
 * Boot Header Signature verification.
 * If Boot Header Signature verification
 * fails then return unique error code saying
 * XFSBL_STAGE3_BOOT_HDR_SIGN_VERIF_ERROR.
 *
 * @param
 *
 * @return
 *
 ******************************************************************************/
u32 XFsbl_PartitionSignVer(XFsblPs *FsblInstancePtr, u64 PartitionOffset,
				u32 PartitionLen, u64 AcOffset, u32 HashLen,
				u32 PartitionNum)
{

	u8 PartitionHash[XFSBL_HASH_TYPE_SHA3] __attribute__ ((aligned (4)));
	u8 * SpkModular = (u8 *)NULL;
	u8 * SpkModularEx = (u8 *)NULL;
	u32 SpkExp = 0;
	u8 * AcPtr = (u8*)(PTRSIZE) AcOffset;
	u32 Status = XFSBL_SUCCESS;
	u32 HashDataLen=0U;
#ifndef XFSBL_PS_DDR
	void * ShaCtx = (void * )NULL;
#endif
	u8 XFsbl_RsaSha3Array[512];

#ifdef XFSBL_SHA2
	sha2_context ShaCtxObj;
	ShaCtx = &ShaCtxObj;
#endif

	XFsbl_Printf(DEBUG_INFO, "Doing Partition Sign verification\r\n");

	/**
	 * hash to be calculated will be total length with AC minus
	 * signature size
	 */
	HashDataLen = PartitionLen - XFSBL_FSBL_SIG_SIZE;

	/* Calculate Partition Hash */
#ifndef XFSBL_PS_DDR
	XFsblPs_PartitionHeader * PartitionHeader;
	u32 DestinationDevice = 0U;
	PartitionHeader =
		&FsblInstancePtr->ImageHeader.PartitionHeader[PartitionNum];
	DestinationDevice = XFsbl_GetDestinationDevice(PartitionHeader);

	if (DestinationDevice == XIH_PH_ATTRB_DEST_DEVICE_PL)
	{
		/**
		 * bitstream partion in DDR less system, Chunk by chunk copy
		 * into OCM and update SHA module
		 */
		u32 NumChunks = 0U;
		u32 RemainingBytes = 0U;
		u32 Index = 0U;
		u32 StartAddrByte = PartitionOffset;

		NumChunks = HashDataLen / READ_BUFFER_SIZE;
		RemainingBytes = (HashDataLen % READ_BUFFER_SIZE);

		/* Start the SHA engine */
		(void)XFsbl_ShaStart(ShaCtx, HashLen);

		XFsbl_Printf(DEBUG_INFO,
			"XFsbl_PartitionVer: NumChunks :%0d, RemainingBytes : %0d \r\n",
			NumChunks, RemainingBytes);

		for(Index = 0; Index < NumChunks; Index++)
		{
			if(XFSBL_SUCCESS !=FsblInstancePtr->DeviceOps.DeviceCopy(
					StartAddrByte, (PTRSIZE)ReadBuffer,
					READ_BUFFER_SIZE))
			{
				XFsbl_Printf(DEBUG_GENERAL,
					"XFsblPartitionVer: Device "
					"to OCM copy of partition failed \r\n");
				XFsbl_Printf(DEBUG_GENERAL,
				"XFsbl_PartitionVer: XFSBL_ERROR_PART_RSA_DECRYPT\r\n");
				Status = XFSBL_ERROR_PART_RSA_DECRYPT;
				goto END;
			}

			XFsbl_ShaUpdate(ShaCtx, (u8 *)ReadBuffer,
						READ_BUFFER_SIZE, HashLen);

			StartAddrByte += READ_BUFFER_SIZE;
		}

		/* Send the residual bytes if Size is not buffer size multiple */
		if(RemainingBytes != 0)
		{
			if(XFSBL_SUCCESS!=FsblInstancePtr->DeviceOps.DeviceCopy(
						StartAddrByte, (PTRSIZE)ReadBuffer,
						RemainingBytes))
			{
				XFsbl_Printf(DEBUG_GENERAL,
				"XFsbl_PartitionVer: XFSBL_ERROR_PART_RSA_DECRYPT\r\n");

				Status = XFSBL_ERROR_PART_RSA_DECRYPT;
				goto END;
			}

			XFsbl_ShaUpdate(ShaCtx, (u8 *)ReadBuffer,
						RemainingBytes, HashLen);
		}

		XFsbl_ShaFinish(ShaCtx, PartitionHash, HashLen);

		/**
		 * Copy Auth. Certificate to OCM buffer location
		 * Assign AcPtr to OCM buffer location
		 */
		if(XFSBL_SUCCESS != FsblInstancePtr->DeviceOps.DeviceCopy(
					(u32)(INTPTR)AcPtr, (PTRSIZE)ReadBuffer,
					XFSBL_AUTH_CERT_MIN_SIZE)) {
			XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_PartitionVer: Flash to OCM copy failed \r\n");
			Status = XFSBL_ERROR_SPK_RSA_DECRYPT;
			goto END;
		}

		/* Repoint Auth. Certificate pointer to start of OCM buffer */
		AcPtr = ReadBuffer;
	}
	else
	{
		XFsbl_Printf(DEBUG_INFO, "partver: sha calc. "
					"for non bs DDR less part \r\n");
		/* SHA calculation for non-bitstream, DDR less partitions */
		XFsbl_ShaDigest((const u8 *)(PTRSIZE)PartitionOffset,
					HashDataLen, PartitionHash, HashLen);
	}
#else
	/* SHA calculation in DDRful systems */
	XFsbl_ShaDigest((const u8 *)(PTRSIZE)PartitionOffset,
			HashDataLen, PartitionHash, HashLen);
#endif

	/* Set SPK pointer */
	AcPtr += (XFSBL_RSA_AC_ALIGN + XFSBL_PPK_SIZE);
	SpkModular = (u8 *)AcPtr;
	AcPtr += XFSBL_SPK_MOD_SIZE;
	SpkModularEx = (u8 *)AcPtr;
	AcPtr += XFSBL_SPK_MOD_EXT_SIZE;
	SpkExp = *((u32 *)AcPtr);
	AcPtr += XFSBL_RSA_AC_ALIGN;

	/* Increment by  SPK Signature pointer */
	AcPtr += XFSBL_SPK_SIG_SIZE;
	/* Increment by  BHDR Signature pointer */
	AcPtr += XFSBL_BHDR_SIG_SIZE;

	XFsbl_Printf(DEBUG_DETAILED,
		"XFsbl_PartVer: Spk Mod %0x, Spk Mod Ex %0x, Spk Exp %0x\r\n",
		SpkModular, SpkModularEx, SpkExp);

	XFsbl_Printf(DEBUG_INFO,
			"Partition Verification done \r\n");

	XSecure_RsaInitialize(&SecureRsa, SpkModular,
				SpkModularEx, (u8 *)&SpkExp);
	/* Decrypt Partition Signature. */
	if(XFSBL_SUCCESS !=
		XSecure_RsaDecrypt(&SecureRsa, AcPtr, XFsbl_RsaSha3Array))
	{
		XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_SpkVer: XFSBL_ERROR_PART_RSA_DECRYPT\r\n");
		Status = XFSBL_ERROR_PART_RSA_DECRYPT;
		goto END;
	}

	/* Authenticate Partition Signature */
	if(XFSBL_SUCCESS != XSecure_RsaSignVerification(XFsbl_RsaSha3Array,
				PartitionHash, HashLen))
	{
		XFsbl_PrintArray(DEBUG_INFO, PartitionHash,
				HashLen, "Calculated Partition Hash");
		XFsbl_PrintArray(DEBUG_INFO, XFsbl_RsaSha3Array,
				512, "RSA decrypted Hash");
		XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_PartVer: XFSBL_ERROR_PART_SIGNATURE\r\n");
		Status = XFSBL_ERROR_PART_SIGNATURE;
		goto END;
	}

END:
	return Status;
}
/**
 * Configure the RSA and SHA for the SPK
 * Signature verification.
 * If SPK Signature verification fails
 * then return unique error code saying
 * XFSBL_STAGE3_SPK_SIGN_VERIF_ERROR.
 *
 * @param
 *
 * @return
 *
 ******************************************************************************/
u32 XFsbl_SpkVer(XFsblPs *FsblInstancePtr , u64 AcOffset, u32 HashLen,
			u32 PartitionNum)
{
	u8 SpkHash[XFSBL_HASH_TYPE_SHA3] __attribute__ ((aligned (4)));
	u8 * PpkModular = (u8 *)NULL;
	u8 * PpkModularEx = (u8 *)NULL;
	u32 PpkExp = 0;
	u8 * AcPtr = (u8*) (PTRSIZE) AcOffset;
	u32 Status = XFSBL_SUCCESS;
	void * ShaCtx = (void * )NULL;
	u8 XFsbl_RsaSha3Array[512];

#ifdef XFSBL_SHA2
	sha2_context ShaCtxObj;
	ShaCtx = &ShaCtxObj;
#endif

#ifndef XFSBL_PS_DDR
	XFsblPs_PartitionHeader * PartitionHeader;
	u32 DestinationDevice = 0U;
	PartitionHeader =
		&FsblInstancePtr->ImageHeader.PartitionHeader[PartitionNum];
	DestinationDevice = XFsbl_GetDestinationDevice(PartitionHeader);

	if (DestinationDevice == XIH_PH_ATTRB_DEST_DEVICE_PL)
	{
		/*
		 * If it is DDR less system, bitstream chunk containing
		 * Authentication Certificate has to be read from boot device
		 */
		if(XFSBL_SUCCESS != FsblInstancePtr->DeviceOps.DeviceCopy(
					(INTPTR)AcPtr, (PTRSIZE)ReadBuffer,
					XFSBL_AUTH_CERT_MIN_SIZE)) {
			XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_SpkVer: Device to OCM copy of certificate failed \r\n");
			Status = XFSBL_ERROR_SPK_RSA_DECRYPT;
			goto END;
		}

		/* Repoint Auth. cert. pointer to OCM buffer beginning*/
		AcPtr = ReadBuffer;
	}
#endif

	/* Re-initialize CSU DMA. This is a workaround and need to be removed */
	Status = XFsbl_CsuDmaInit();
	if (XST_SUCCESS != Status) {
		goto END;
	}

	(void)XFsbl_ShaStart(ShaCtx, HashLen);

	/* Hash the PPK + SPK choice */
	XFsbl_ShaUpdate(ShaCtx, AcPtr, 8, HashLen);

	/* Set PPK pointer */
	AcPtr += XFSBL_RSA_AC_ALIGN;
	PpkModular = (u8 *)AcPtr;
	AcPtr += XFSBL_PPK_MOD_SIZE;
	PpkModularEx = (u8 *)AcPtr;
	AcPtr += XFSBL_PPK_MOD_EXT_SIZE;
	PpkExp = *((u32 *)AcPtr);
	AcPtr += XFSBL_RSA_AC_ALIGN;

	XFsbl_Printf(DEBUG_DETAILED,
		"XFsbl_SpkVer: Ppk Mod %0x, Ppk Mod Ex %0x, Ppk Exp %0x\r\n",
		PpkModular, PpkModularEx, PpkExp);

	/* Calculate SPK + Auth header Hash */
	XFsbl_ShaUpdate(ShaCtx, (u8 *)AcPtr, XFSBL_SPK_SIZE, HashLen);

	XFsbl_ShaFinish(ShaCtx, (u8 *)SpkHash, HashLen);

	/* Set SPK Signature pointer */
	AcPtr += XFSBL_SPK_SIZE;

	XSecure_RsaInitialize(&SecureRsa, PpkModular,
				PpkModularEx, (u8 *)&PpkExp);

	/* Decrypt SPK Signature */
	if(XFSBL_SUCCESS !=
		XSecure_RsaDecrypt(&SecureRsa, AcPtr, XFsbl_RsaSha3Array))
	{
		XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_SpkVer: XFSBL_ERROR_SPK_RSA_DECRYPT\r\n");
		Status = XFSBL_ERROR_SPK_RSA_DECRYPT;
		goto END;
	}

	/* Authenticate SPK Signature */
	if(XFSBL_SUCCESS != XSecure_RsaSignVerification(XFsbl_RsaSha3Array,
							SpkHash, HashLen))
	{
		XFsbl_PrintArray(DEBUG_INFO, SpkHash,
				HashLen, "Calculated SPK Hash");
		XFsbl_PrintArray(DEBUG_INFO, XFsbl_RsaSha3Array,
				512, "RSA decrypted Hash");
		XFsbl_Printf(DEBUG_GENERAL,
			"XFsbl_SpkVer: XFSBL_ERROR_SPK_SIGNATURE\r\n");
		Status = XFSBL_ERROR_SPK_SIGNATURE;
	}

END:
	return Status;
}
/******************************************************************************
*
* This function performs authentication and RSA signature verification for
* each block.
*
* @param	PartitionParams is a pointer to XFsblPs_PlPartition
* @param	BlockAdrs is the block start address
* @param	BlockSize is the block size
* @param	AcOffset holds authentication certificate's address
* @param	NoOfChunks for the provided block
*
* @return
* 		Error code on failure
* 		XFSBL_SUCESS on success
*
* @note		None
*
******************************************************************************/
static u32 XFsbl_PlSignVer(XFsblPs_PlPartition *PartitionParams,
		UINTPTR BlockAdrs, u32 BlockSize, u8 *AcOffset, u32 NoOfChunks)
{

	u8 PartitionHash[XFSBL_HASH_TYPE_SHA3]={0U};
	u8 * SpkModular;
	u8* SpkModularEx;
	u32 SpkExp;
	u8 * AcPtr = (u8*)AcOffset;
	u32 Status;
	u32 HashDataLen = BlockSize;
	u8 XFsbl_RsaSha3Array[512] = {0U};
	s32 SStatus;
	u8 *ChunksHash = PartitionParams->PlAuth.HashsOfChunks;
	XSecure_Sha3 SecureSha3={0U};
	XSecure_Rsa SecureRsa={0U};
	u32 Index;
	u32 Len = PartitionParams->ChunkSize;
	u64 Offset;

	/* Start the SHA engine */
	if (XSECURE_HASH_TYPE_SHA3 == PartitionParams->PlAuth.AuthType) {
		XCsuDma_Configure ConfigurValues = {0};
		/* this needs to be modified at encryption */
		XCsuDma_GetConfig(PartitionParams->CsuDmaPtr,
			XCSUDMA_SRC_CHANNEL, &ConfigurValues);

		ConfigurValues.EndianType = 0U;

		XCsuDma_SetConfig(PartitionParams->CsuDmaPtr,
			XCSUDMA_SRC_CHANNEL, &ConfigurValues);
		(void)XSecure_Sha3Initialize(&SecureSha3,
			PartitionParams->CsuDmaPtr);

		XSecure_Sha3Start(&SecureSha3);
	}

	/* SHA calculation */
	for (Index = 0; Index < NoOfChunks; Index++) {
		/*
		 * If the block is the last it may not be complete block size
		 * mentioned so will break if it is less than chunk size
		 */
		if ((Index == NoOfChunks-1) ||
			(HashDataLen < PartitionParams->ChunkSize)) {
			Len = HashDataLen;
		}
		Offset = (u64)BlockAdrs +
			(u64)(PartitionParams->ChunkSize * Index);

		Status = XFsbl_CopyData(PartitionParams,
			PartitionParams->ChunkBuffer, (u8 *)(UINTPTR)Offset, Len);
		if (Status != XFSBL_SUCCESS) {
			return Status;
		}

		XSecure_Sha3Update(&SecureSha3,
			PartitionParams->ChunkBuffer, Len);
		XSecure_Sha3_ReadHash(&SecureSha3,
				(u8 *)ChunksHash);

		if (Index+1 < NoOfChunks) {
			HashDataLen =
				HashDataLen - PartitionParams->ChunkSize;
			ChunksHash =
				ChunksHash + PartitionParams->PlAuth.AuthType;
		}

	}

	/* Calculate hash for (AC - signature size) */
	XSecure_Sha3Update(&SecureSha3, (u8 *)AcOffset,
		(XFSBL_AUTH_CERT_MIN_SIZE - XFSBL_FSBL_SIG_SIZE));
	XSecure_Sha3Finish(&SecureSha3, (u8 *)PartitionHash);

	/* Set SPK pointer */
	AcPtr += (XFSBL_RSA_AC_ALIGN + XFSBL_PPK_SIZE);
	SpkModular = AcPtr;
	AcPtr += XFSBL_SPK_MOD_SIZE;
	SpkModularEx = AcPtr;
	AcPtr += XFSBL_SPK_MOD_EXT_SIZE;
	SpkExp = *((u32 *)AcPtr);
	AcPtr += XFSBL_RSA_AC_ALIGN;

	/* Increment by  SPK Signature pointer */
	AcPtr += XFSBL_SPK_SIG_SIZE;
	/* Increment by  BHDR Signature pointer */
	AcPtr += XFSBL_BHDR_SIG_SIZE;
	if((SpkModular != NULL) && (SpkModularEx != NULL)) {
		XFsbl_Printf(DEBUG_DETAILED,
		"XFsbl_PartVer: Spk Mod %0x, Spk Mod Ex %0x, Spk Exp %0x\r\n",
		SpkModular, SpkModularEx, SpkExp);
	}

	SStatus = XSecure_RsaInitialize(&SecureRsa, SpkModular,
				SpkModularEx, (u8 *)&SpkExp);
	if (SStatus != XFSBL_SUCCESS) {
		Status = XFSBL_ERROR_RSA_INITIALIZE;
		XFsbl_Printf(DEBUG_INFO,
		"XFSBL_ERROR_RSA_INITIALIZE at PL verification\r\n");
		goto END;
	}
	/* Decrypt Partition Signature. */
	if(XFSBL_SUCCESS !=
		XSecure_RsaDecrypt(&SecureRsa, AcPtr, XFsbl_RsaSha3Array)) {
		XFsbl_Printf(DEBUG_INFO, "XFsbl_SpkVer: "
			"XFSBL_ERROR_PART_RSA_DECRYPT at PL verification\r\n");
		Status = XFSBL_ERROR_PART_RSA_DECRYPT;
		goto END;
	}


	/* Authenticate Partition Signature */
	if(XFSBL_SUCCESS != XSecure_RsaSignVerification(XFsbl_RsaSha3Array,
			PartitionHash, PartitionParams->PlAuth.AuthType))
	{
		XFsbl_PrintArray(DEBUG_INFO, PartitionHash,
			PartitionParams->PlAuth.AuthType,
			"Calculated Partition Hash at PL verification");
		XFsbl_PrintArray(DEBUG_INFO, XFsbl_RsaSha3Array,
			512, "RSA decrypted Hash at PL verification");
		Status = XFSBL_FAILURE;
		goto END;
	}

	Status = XFSBL_SUCCESS;

END:
	return Status;
}