/********************************************************************************/
/*										*/
/*			Sample Crypto Utilities					*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/* (c) Copyright IBM Corporation 2017 - 2022.					*/
/*										*/
/* All rights reserved.								*/
/* 										*/
/* Redistribution and use in source and binary forms, with or without		*/
/* modification, are permitted provided that the following conditions are	*/
/* met:										*/
/* 										*/
/* Redistributions of source code must retain the above copyright notice,	*/
/* this list of conditions and the following disclaimer.			*/
/* 										*/
/* Redistributions in binary form must reproduce the above copyright		*/
/* notice, this list of conditions and the following disclaimer in the		*/
/* documentation and/or other materials provided with the distribution.		*/
/* 										*/
/* Neither the names of the IBM Corporation nor the names of its		*/
/* contributors may be used to endorse or promote products derived from		*/
/* this software without specific prior written permission.			*/
/* 										*/
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS		*/
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT		*/
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR	*/
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT		*/
/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,	*/
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT		*/
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,	*/
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY	*/
/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT		*/
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE	*/
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.		*/
/********************************************************************************/

#ifndef CRYPTUTILS_H
#define CRYPTUTILS_H

/* Windows 10 crypto API clashes with openssl */
#ifdef TPM_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <windows.h>
#endif

/* TPM_TSS_NO_OPENSSL is a legacy macro.  cryptoutils was exposing several OpenSSL specific
   functions.  They are not available for other crypto libraries.  For OpenSSL, they are available
   but deprecated.  */

#ifndef TPM_TSS_NO_OPENSSL
#include <openssl/rand.h>
#include <openssl/pem.h>
#endif	/* TPM_TSS_NO_OPENSSL */

#ifdef TPM_TSS_MBEDTLS
#include <mbedtls/pk.h>
#endif	/* TPM_TSS_MBEDTLS */

#include <ibmtss/tss.h>

#ifdef __cplusplus
extern "C" {
#endif

    /*
      crypto library independent functions
    */

    void getCryptoLibrary(const char **name);
    
    TPM_RC convertPemToRsaPrivKey(void **rsaKey,
				  const char *pemKeyFilename,
				  const char *password);
    TPM_RC convertRsaKeyToPublicKeyBin(int 	*modulusBytes,
				       uint8_t 	**modulusBin,
				       void	*rsaKey);
    TPM_RC convertRsaKeyToPublic(TPM2B_PUBLIC 		*objectPublic,
				 int			keyType,
				 TPMI_ALG_SIG_SCHEME 	scheme,
				 TPMI_ALG_HASH 		nalg,
				 TPMI_ALG_HASH		halg,
				 void			*rsaKey);
    TPM_RC convertRsaPemToKeyPair(TPM2B_PUBLIC 		*objectPublic,
				  TPM2B_PRIVATE 	*objectPrivate,
				  int			keyType,
				  TPMI_ALG_SIG_SCHEME 	scheme,
				  TPMI_ALG_HASH 	nalg,
				  TPMI_ALG_HASH		halg,
				  const char 		*pemKeyFilename,
				  const char 		*password);
    TPM_RC convertRsaDerToKeyPair(TPM2B_PUBLIC 		*objectPublic,
				  TPM2B_SENSITIVE 	*objectSensitive,
				  int			keyType,
				  TPMI_ALG_SIG_SCHEME 	scheme,
				  TPMI_ALG_HASH 	nalg,
				  TPMI_ALG_HASH		halg,
				  const char		*derKeyFilename,
				  const char 		*password);
    TPM_RC convertRsaDerToPublic(TPM2B_PUBLIC 		*objectPublic,
				 int			keyType,
				 TPMI_ALG_SIG_SCHEME 	scheme,
				 TPMI_ALG_HASH 		nalg,
				 TPMI_ALG_HASH		halg,
				 const char		*derKeyFilename);
    TPM_RC convertRsaPemToPublic(TPM2B_PUBLIC 		*objectPublic,
				 int			keyType,
				 TPMI_ALG_SIG_SCHEME 	scheme,
				 TPMI_ALG_HASH 		nalg,
				 TPMI_ALG_HASH		halg,
				 const char 		*pemKeyFilename);
    TPM_RC convertRsaPrivateKeyBinToPrivate(TPM2B_PRIVATE 	*objectPrivate,
					    TPM2B_SENSITIVE 	*objectSensitive,
					    int 		privateKeyBytes,
					    uint8_t 		*privateKeyBin,
					    const char 		*password);
    TPM_RC convertRsaPublicKeyBinToPublic(TPM2B_PUBLIC 		*objectPublic,
					  int			keyType,
					  TPMI_ALG_SIG_SCHEME 	scheme,
					  TPMI_ALG_HASH 	nalg,
					  TPMI_ALG_HASH		halg,
					  int 			modulusBytes,
					  uint8_t 		*modulusBin);
    TPM_RC convertPublicToPEM(const TPM2B_PUBLIC *inPublic,
			      const char *pemFilename);

    TPM_RC signRSAFromRSA(uint8_t *signature, size_t *signatureLength,
			  size_t signatureSize,
			  const uint8_t *digest, size_t digestLength,
			  TPMI_ALG_HASH hashAlg,
			  void *rsaKey);
    TPM_RC verifySignatureFromPem(unsigned char *message,
				  unsigned int messageSize,
				  TPMT_SIGNATURE *tSignature,
				  TPMI_ALG_HASH halg,
				  const char *pemFilename);
    TPM_RC verifyRSASignatureFromRSA(unsigned char *message,
				     unsigned int messageSize,
				     TPMT_SIGNATURE *tSignature,
				     TPMI_ALG_HASH halg,
				     void *rsaPubKey);
    TPM_RC verifyRSASignatureFromRSA3(const unsigned char *message,
				      unsigned int messageSize,
				      TPMT_SIGNATURE *tSignature,
				      TPMI_ALG_HASH halg,
				      void *rsaPubKey);
    TPM_RC verifySignatureFromHmacKey(unsigned char *message,
				      unsigned int messageSize,
				      TPMT_SIGNATURE *tSignature,
				      TPMI_ALG_HASH halg,
				      const char *hmacKeyFilename);

    TPM_RC convertRsaBinToTSignature(TPMT_SIGNATURE *tSignature,
				     TPMI_ALG_HASH halg,
				     const uint8_t *signatureBin,
				     size_t signatureBinLen);

    /* Some OpenSSL builds do not include ECC */

#ifndef TPM_TSS_NOECC

    TPM_RC convertEcPemToKeyPair(TPM2B_PUBLIC 		*objectPublic,
				 TPM2B_PRIVATE 		*objectPrivate,
				 int			keyType,
				 TPMI_ALG_SIG_SCHEME 	scheme,
				 TPMI_ALG_HASH 		nalg,
				 TPMI_ALG_HASH		halg,
				 const char 		*pemKeyFilename,
				 const char 		*password);
    TPM_RC convertEcPemToPublic(TPM2B_PUBLIC 		*objectPublic,
				int			keyType,
				TPMI_ALG_SIG_SCHEME 	scheme,
				TPMI_ALG_HASH 		nalg,
				TPMI_ALG_HASH		halg,
				const char		*pemKeyFilename);
    TPM_RC convertEcDerToKeyPair(TPM2B_PUBLIC 		*objectPublic,
				 TPM2B_SENSITIVE 	*objectSensitive,
				 int			keyType,
				 TPMI_ALG_SIG_SCHEME 	scheme,
				 TPMI_ALG_HASH 		nalg,
				 TPMI_ALG_HASH		halg,
				 const char		*derKeyFilename,
				 const char 		*password);
    TPM_RC convertEcDerToPublic(TPM2B_PUBLIC 		*objectPublic,
				int			keyType,
				TPMI_ALG_SIG_SCHEME 	scheme,
				TPMI_ALG_HASH 		nalg,
				TPMI_ALG_HASH		halg,
				const char		*derKeyFilename);
    TPM_RC convertEcPrivateKeyBinToPrivate(TPM2B_PRIVATE 	*objectPrivate,
					   TPM2B_SENSITIVE 	*objectSensitive,
					   int 			privateKeyBytes,
					   uint8_t 		*privateKeyBin,
					   const char 		*password);
    TPM_RC convertEcBinToTSignature(TPMT_SIGNATURE 	*tSignature,
				    TPMI_ALG_HASH 	halg,
				    const uint8_t 	*signatureBin,
				    size_t 		signatureBinLen);
    
#endif	/* TPM_TSS_NOECC */
    
    /*
      OpenSSL specific functions

      These are not intended for general use.
    */
   
#ifndef TPM_TSS_NO_OPENSSL

/* Some functions add const to parameters as of openssl 1.1.0 */

#if OPENSSL_VERSION_NUMBER < 0x10100000
#define OSSLCONST
#else
#define OSSLCONST const
#endif

#if OPENSSL_VERSION_NUMBER < 0x10100000
    int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
    void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
    const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x);
    void RSA_get0_key(const RSA *rsaKey,
		      const BIGNUM **n,
		      const BIGNUM **e,
		      const BIGNUM **d);
    void RSA_get0_factors(const RSA *rsaKey,
			  const BIGNUM **p,
			  const BIGNUM **q);
    int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm);
    int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm);
    EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key);
#endif	/* pre openssl 1.1 */

#if OPENSSL_VERSION_NUMBER < 0x10002000
    void X509_get0_signature(OSSLCONST ASN1_BIT_STRING **psig,
			     OSSLCONST X509_ALGOR **palg, const X509 *x);
#endif	/* pre openssl 1.0.2 */

    TPM_RC convertPemToEvpPrivKey(EVP_PKEY **evpPkey,
				  const char *pemKeyFilename,
				  const char *password);
    TPM_RC convertPemToEvpPubKey(EVP_PKEY **evpPkey,
				 const char *pemKeyFilename);
    TPM_RC convertEvpPubkeyToPem(EVP_PKEY *evpPubkey,
				 const char *pemFilename);
    TPM_RC convertBin2Bn(BIGNUM **bn,
			 const unsigned char *bin,
			 unsigned int bytes);
    
    TPM_RC convertEvpPkeyToRsakey(RSA **rsaKey,
				  EVP_PKEY *evpPkey);
#if OPENSSL_VERSION_NUMBER < 0x30000000
    TPM_RC convertRsaKeyToPrivateKeyBin(int 		*privateKeyBytes,
					uint8_t 	**privateKeyBin,
					const RSA	*rsaKey);
    TPM_RC convertRsaKeyToPrivate(TPM2B_PRIVATE 	*objectPrivate,
				  TPM2B_SENSITIVE 	*objectSensitive,
				  RSA 			*rsaKey,
				  const char 		*password);
    TPM_RC getRsaKeyParts(const BIGNUM **n,
			  const BIGNUM **e,
			  const BIGNUM **d,
			  const BIGNUM **p,
			  const BIGNUM **q,
			  const RSA	*rsaKey);
#else
    TPM_RC convertRsaKeyToPrivateKeyBin(int 		*privateKeyBytes,
					uint8_t 	**privateKeyBin,
					const EVP_PKEY *rsaKey);
    TPM_RC convertRsaKeyToPrivate(TPM2B_PRIVATE 	*objectPrivate,
				  TPM2B_SENSITIVE 	*objectSensitive,
				  EVP_PKEY 		*rsaKey,
				  const char 		*password);
    TPM_RC getRsaKeyParts(const BIGNUM **n,
			  const BIGNUM **e,
			  const BIGNUM **d,
			  const BIGNUM **p,
			  const BIGNUM **q,
			  const EVP_PKEY *rsaKey);
#endif
    int getRsaPubkeyAlgorithm(EVP_PKEY *pkey);
    TPM_RC convertRsaPublicToEvpPubKey(EVP_PKEY **evpPubkey,
				       const TPM2B_PUBLIC_KEY_RSA *tpm2bRsa);
    TPM_RC verifyRSASignatureFromEvpPubKey(unsigned char *message,
					   unsigned int messageSize,
					   TPMT_SIGNATURE *tSignature,
					   TPMI_ALG_HASH halg,
					   EVP_PKEY *evpPkey);

#ifndef TPM_TSS_NOECC
    TPM_RC convertEvpPkeyToEckey(EC_KEY **ecKey,
				 EVP_PKEY *evpPkey);
    TPM_RC convertEcPublicKeyBinToPublic(TPM2B_PUBLIC 		*objectPublic,
					 int			keyType,
					 TPMI_ALG_SIG_SCHEME 	scheme,
					 TPMI_ALG_HASH 		nalg,
					 TPMI_ALG_HASH		halg,
					 TPMI_ECC_CURVE 	curveID,
					 int 			modulusBytes,
					 uint8_t 		*modulusBin);
#if OPENSSL_VERSION_NUMBER < 0x30000000
    TPM_RC convertEcKeyToPrivate(TPM2B_PRIVATE 		*objectPrivate,
				 TPM2B_SENSITIVE 	*objectSensitive,
				 EC_KEY 		*ecKey,
				 const char 		*password);
    TPM_RC convertEcKeyToPublicKeyBin(int 		*modulusBytes,
				      uint8_t 		**modulusBin,
				      const EC_KEY 	*ecKey);
    TPM_RC convertEcKeyToPrivateKeyBin(int 		*privateKeyBytes,
				       uint8_t 		**privateKeyBin,
				       const EC_KEY 	*ecKey);
    TPM_RC convertEcKeyToPublic(TPM2B_PUBLIC 		*objectPublic,
				int			keyType,
				TPMI_ALG_SIG_SCHEME 	scheme,
				TPMI_ALG_HASH 		nalg,
				TPMI_ALG_HASH		halg,
				EC_KEY 			*ecKey);
#else
    TPM_RC convertEcKeyToPrivate(TPM2B_PRIVATE 		*objectPrivate,
				 TPM2B_SENSITIVE 	*objectSensitive,
				 EVP_PKEY 		*ecKey,
				 const char 		*password);
    TPM_RC convertEcKeyToPublicKeyBin(int 		*modulusBytes,
				      uint8_t 		**modulusBin,
				      const EVP_PKEY 	*ecKey);
    TPM_RC convertEcKeyToPrivateKeyBin(int 		*privateKeyBytes,
				       uint8_t 		**privateKeyBin,
				       const EVP_PKEY 	*ecKey);
    TPM_RC convertEcKeyToPublic(TPM2B_PUBLIC 		*objectPublic,
				int			keyType,
				TPMI_ALG_SIG_SCHEME 	scheme,
				TPMI_ALG_HASH 		nalg,
				TPMI_ALG_HASH		halg,
				EVP_PKEY 		*ecKey);

#endif
    TPM_RC convertEcPublicToEvpPubKey(EVP_PKEY **evpPubkey,
				      const TPMS_ECC_POINT *tpmsEccPoint);
    TPM_RC convertEcTPMTPublicToEvpPubKey(EVP_PKEY **evpPubkey,
					  const TPMT_PUBLIC *tpmtPublic);
    TPM_RC verifyEcSignatureFromEvpPubKey(unsigned char *message,
					  unsigned int messageSize,
					  TPMT_SIGNATURE *tSignature,
					  EVP_PKEY *evpPkey);
    
#endif /* TPM_TSS_NOECC */
#endif /* TPM_TSS_NO_OPENSSL */

    /*
      mbedtls specific functions

      These are not intended for general use, but are used by ekutils.c
    */

#ifdef TPM_TSS_MBEDTLS
    
    TPM_RC convertPkToRsaKey(mbedtls_rsa_context **rsaCtx,
			     mbedtls_pk_context *pkCtx);
    TPM_RC convertPkToEckey(mbedtls_ecp_keypair **ecCtx,
			    mbedtls_pk_context	*pkCtx);
    TPM_RC convertEcKeyToPublicKeyXYBin(size_t			*xBytes,
					uint8_t 		**xBin,
					size_t			*yBytes,
					uint8_t 		**yBin,
					mbedtls_ecp_keypair 	*ecKp);

#endif	/* TPM_TSS_MBEDTLS */
    
#ifdef __cplusplus
}
#endif

#endif
