/*
 * An example of OpenSSL PEM encoding/decoding for proprietary types
 *
 * Copyright (c) 2009 Mounir IDRASSI <mounir.idrassi@idrix.fr>. All rights reserved.
 *
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 */

#include <stdio.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/asn1t.h>


/*
 * Our sample key type definition
 */
typedef struct
{
   BIGNUM* s;
   BIGNUM* d;
} SampleKeyType;


/*
 * Defines used by PEM support handling
 */

#define PEM_STRING_SAMPLE_KEY	"SAMPLE KEY" /* This will appear in the header and the footer of the PEM file */

SampleKeyType *SampleKeyType_new(void);
SampleKeyType *SampleKey_dup(SampleKeyType *rsa);
void           SampleKeyType_free(SampleKeyType* key);

SampleKeyType *d2i_SampleKey_fp(FILE *fp,SampleKeyType **key);
int            i2d_SampleKey_fp(FILE *fp,SampleKeyType *key);
SampleKeyType *d2i_SampleKey_bio(BIO *bp,SampleKeyType **key);
int            i2d_SampleKey_bio(BIO *bp,SampleKeyType *key);

DECLARE_PEM_rw_const(SampleKey, SampleKeyType)

DECLARE_ASN1_ENCODE_FUNCTIONS_const(SampleKeyType, SampleKey)

int            samplekey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it);

/*
 * Start of the implementation of PEM handling
 */

IMPLEMENT_PEM_rw_const(SampleKey, SampleKeyType, PEM_STRING_SAMPLE_KEY, SampleKey)

SampleKeyType *SampleKeyType_new(void)
{
   SampleKeyType *ret = (SampleKeyType *)OPENSSL_malloc(sizeof(SampleKeyType));
   if (ret)
   {
      ret->s = NULL;
      ret->d = NULL;
   }

	return ret;
}

void SampleKeyType_free(SampleKeyType* key)
{
   if (key)
   {
      if (key->s)
         BN_clear_free(key->s);
      if (key->d)
         BN_clear_free(key->d);
      OPENSSL_free(key);
   }
}

int samplekey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it)
{
   if(operation == ASN1_OP_NEW_PRE) {
      *pval = (ASN1_VALUE *)SampleKeyType_new();
      if(*pval) return 2;
      return 0;
   }
   else if(operation == ASN1_OP_FREE_PRE) {
      SampleKeyType_free((SampleKeyType *)*pval);
      *pval = NULL;
      return 2;
   }
   return 1;
}

ASN1_SEQUENCE_cb(SampleKey, samplekey_cb) = {
   ASN1_SIMPLE(SampleKeyType, s, BIGNUM),
   ASN1_SIMPLE(SampleKeyType, d, BIGNUM),
} ASN1_SEQUENCE_END_cb(SampleKeyType, SampleKey)

IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(SampleKeyType, SampleKey, SampleKey)

SampleKeyType *SampleKey_dup(SampleKeyType *key)
{
   return ASN1_item_dup(ASN1_ITEM_rptr(SampleKey), key);
}

SampleKeyType *d2i_SampleKey_fp(FILE *fp, SampleKeyType **key)
{
   return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SampleKey), fp, key);
}

int i2d_SampleKey_fp(FILE *fp, SampleKeyType *key)
{
   return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SampleKey), fp, key);
}

SampleKeyType *d2i_SampleKey_bio(BIO *bp, SampleKeyType **key)
{
   return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SampleKey), bp, key);
}

int i2d_SampleKey_bio(BIO *bp, SampleKeyType *key)
{
   return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SampleKey), bp, key);
}


/*
 * Test program
 */
int main(int argc, char** argv)
{
   FILE *fp = NULL;
   SampleKeyType  *key = SampleKeyType_new();
   SampleKeyType  *key2 = NULL;
   
   /*
    * initialize with sample values
    */
   key->s = BN_new();
   key->d = BN_new();
   BN_hex2bn(&key->s, "ABABC");
   BN_hex2bn(&key->d, "707219");

   /*
    * write to file
    */
   fp = fopen("key.pem", "wt");
   if (fp)
   {
      PEM_write_SampleKey(fp, key);
      fclose(fp);
   }

   /*
    * read from file
    */
   fp = fopen("key.pem", "rt");
   if (fp)
   {
      key2 = PEM_read_SampleKey(fp, NULL, NULL, NULL);
      if (key2)
      {
         if ( BN_cmp(key->s, key2->s) || BN_cmp(key->d, key2->d) )
         {
            printf("error : key mismatch\n");
         }
         else
         {
            printf("Test OK!\n");
         }

         SampleKeyType_free(key2);
      }
      else
      {
         printf("failed to read key from file.\n");
      }
   }

   SampleKeyType_free(key);

   return 0;
}

