/* MakeAliceRsaPss.c */

/* Create an X.509 certificate for Alice using RSA-PSS-SHA256 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "diCrPKI.h"

#ifdef NDEBUG
/* Make sure assertion testing is turned on */
#undef NDEBUG
#endif
#include <assert.h>

/* Link with `diCrPKI.lib`.
 * In MSVC set
 * Configuration Properties > Linker > Input > Additional Dependencies=diCrPKI.lib;
 */

static void pr_x509dump(const char *certname);

int main(void)
{
	/* Required input files */
	const char *issuercert = "CarlRSAPssSelf.cer";	// Carl the CA's certificate
	const char *issuerkey = "CarlRSAPSS.p8e";		// Carl's encrypted private key
	const char *subjectpubkey = "AliceRSAPSS.pub";	// Alice's public key
	/* Name of output certificate file to be created */
	const char *certfile = "AliceRsaPssSignByCarl.cer";
	char ski[PKI_SHA1_CHARS + 1];	/* Buffer for SHA-1 digest of public key*/
	char extnbuf[512];
	char *query;
	char qrybuf[128];
	char *keystr;
	long r, nchars;

	/* Other fixed parameters */
	const char *dn = "CN=AliceRSA;O=PSS;";	// Distinguished name
	/* Set a fixed serial number and validity dates */
	const char *extns = 
		"rfc822name=AliceRSA@example.com;" 
		"serialNumber=#x47346BC7800056BC11D36E2EC410B3B0;" 
		"notBefore=2018-04-01;" 
		"notAfter=2039-12-31;"
		"subjectKeyIdentifier=" // Value to be added
		;
	/* Set required key usage flags */
	long ku = PKI_X509_KEYUSAGE_DIGITALSIGNATURE | PKI_X509_KEYUSAGE_NONREPUDIATION;

	/* Show we have the latest version of the core DLL */
	printf("PKI_Version=%ld\n", PKI_Version(0, 0));

	/* Compute SubjectKeyIdentifier from public key file (see RFC5280 s4.2.1.2) 
	(1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
           value of the BIT STRING subjectPublicKey (excluding the tag,
           length, and number of unused bits).
	-- this is exactly the contents of the RSAPublicKey public key file created
	-- by this Toolkit.
	*/
	r = HASH_HexFromFile(ski, sizeof(ski) - 1, subjectpubkey, PKI_HASH_SHA1);
	printf("SKI=%s\n", ski);

	/* Compose Extensions parameter by appending the SKI value in hex */
	strcpy_s(extnbuf, sizeof(extnbuf), extns);
	strcat_s(extnbuf, sizeof(extnbuf), ski);
	strcat_s(extnbuf, sizeof(extnbuf), ";");
	printf("extns=[%s]\n", extnbuf);

	/* Create X.509 certificate file 
	 * using RSA-PSS-256 signature algorithm but setting the PSS parameter salt length to zero 
	 * so we get the same result each time.
	 * Hint: this is for demonstration purposes. Use the default salt length in practice.
	 */
	r = X509_MakeCert(certfile, issuercert, subjectpubkey, issuerkey, 0, 0, dn, extnbuf, ku, "password", 
		PKI_SIG_RSA_PSS_SHA256 | PKI_PSS_SALTLEN_ZERO | PKI_X509_AUTHKEYID);
	printf("X509_MakeCert(0 returns %ld (expecting 0)\n", r);
	assert(0 == r);
	
	/* Dump details about this new certificate */
	pr_x509dump(certfile);
	/* Expected output:
	FILE: AliceRsaPssSignByCarl.cer
	X.509 CERTIFICATE
	Version: 3
	Serial Number:
	  #x47346BC7800056BC11D36E2EC410B3B0
	Issuer:
	  CN=CarlRSA;O=PSS
	Subject:
	  CN=AliceRSA;O=PSS
	-----[cut]-----
	SHA-256 Thumbprint:
	4a4783c4ed1be9052a3170f70ae1e24dde5da79a9df816e6b1f522203bccfbbf
	*/

	/* Query some information from it */
	/* Expected output:
	X509_QueryCert(signatureAlgorithm)=rsaPSS
	X509_QueryCert(hashAlgorithm)=sha256
	X509_QueryCert(pssParams)=sha256/mgf1SHA256/0/trailerFieldBC	
	*/
	query = "signatureAlgorithm";
	X509_QueryCert(qrybuf, sizeof(qrybuf) - 1, certfile, query, 0);
	printf("X509_QueryCert(%s)=%s\n", query, qrybuf);
	query = "hashAlgorithm";
	X509_QueryCert(qrybuf, sizeof(qrybuf) - 1, certfile, query, 0);
	printf("X509_QueryCert(%s)=%s\n", query, qrybuf);
	query = "pssParams";
	X509_QueryCert(qrybuf, sizeof(qrybuf) - 1, certfile, query, 0);
	printf("X509_QueryCert(%s)=%s\n", query, qrybuf);

	/* Show we can extract Alice's public key from it */
	nchars = RSA_ReadAnyPublicKey(NULL, 0, certfile, 0);
	assert(nchars > 0);
	// Read into an internal key string
	keystr = malloc(nchars + 1);
	assert(keystr);
	nchars = RSA_ReadAnyPublicKey(keystr, nchars, certfile, 0);
	printf("Key length=%ld bits\n", RSA_KeyBits(keystr));	// Key length=2048 bits
	free(keystr);

}

/// Dump details of X.509 file to stdout
static void pr_x509dump(const char *certname)
{
	char *buf;
	long nchars;

	printf("FILE: %s\n", certname);
	// New function in v12
	nchars = X509_TextDumpToString(NULL, 0, certname, 0);
	if (nchars <= 0) {
		printf("**ERROR %ld\n", nchars);
		return;
	}
	buf = malloc(nchars + 1);
	assert(buf);
	nchars = X509_TextDumpToString(buf, nchars, certname, 0);
	printf("%s\n", buf);
	free(buf);
}