text: /* Copyright (c) 1998,2011,2014 Apple Inc.  All Rights Reserved.
   *
   * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
   * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
   * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE, INC. AND THE
   * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
   * INC.  ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
   * EXPOSE YOU TO LIABILITY.
   ***************************************************************************
   *
   * feeCipherFile.c
   *
   * Revision History
   * ----------------
   * 10/06/98		ap
   *	Changed to compile with C++.
   * 05 Feb 97 at Apple
   *	Modified to use portable byte representation.
   * 23 Oct 96 at NeXT
   *	Created.
   */
  
  #include "feeCipherFile.h"
  #include "falloc.h"
  #include "platform.h"
  #include "feeDebug.h"
  #include "byteRep.h"
  
  #ifndef	NULL
  #define NULL ((void *)0)
  #endif	/* NULL */
  
  /*
   * These must match constants of same name in CipherFileAtom.java.
   */
  #define CFILE_MAGIC		0xfeecf111
  #define CFILE_VERSION		1
  #define CFILE_MIN_VERSION	1
  
  /*
   * Format of a feeCipherFile header.
   * Offsets and lengths refer to locations of components in cFileInst.dataRep.
   * This struct appears at the start of a feeCipherFile data representation.
   */
  typedef struct {
  	unsigned		magic;
  	unsigned		version;
  	unsigned		minVersion;
  	unsigned		totalLength;		// equals dataRepLen
  	cipherFileEncrType	encrType;
  	unsigned		cipherTextOffset;	// offset of ciphertext
  	unsigned		cipherTextLen;		// in bytes
  	unsigned		sendPubKeyDataOffset;	// optional
  	unsigned		sendPubKeyDataLen;
  	unsigned		otherKeyDataOffset;	// optional
  	unsigned		otherKeyDataLen;
  	unsigned		sigDataOffset;		// optional
  	unsigned		sigDataLen;		// 0 means no signature
  	unsigned		userData;
  } cFileHeader;
  
  /*
   * Private data, represented by a feeCipherFile handle.
   */
  typedef struct {
  	cFileHeader 	header;
  	unsigned char 	*dataRep;		// raw data
  	unsigned 	dataRepLen;
  } cFileInst;
  
  static unsigned lengthOfByteRepCfileHdr(void);
  static unsigned cfileHdrToByteRep(cFileHeader *hdr,
  	unsigned char *s);
  static void byteRepToCfileHdr(const unsigned char *s,
  	cFileHeader *hdr);
  
  
  /*
   * alloc, free cFileInst
   */
  static cFileInst *cFileInstAlloc()
  {
  	cFileInst *cfinst = (cFileInst *) fmalloc(sizeof(cFileInst));
  
  	bzero(cfinst, sizeof(cFileInst));
  	return cfinst;
  }
  
  static void cFileInstFree(cFileInst *cfinst)
  {
  	if(cfinst->dataRep) {
  		ffree(cfinst->dataRep);
  	}
  	ffree(cfinst);
  }
  
  /*
   * Alloc and return a new feeCipherFile object associated with the specified
   * data.
   */
  feeCipherFile feeCFileNewFromCipherText(cipherFileEncrType encrType,
  	const unsigned char *cipherText,
  	unsigned cipherTextLen,
  	const unsigned char *sendPubKeyData,	// optional
  	unsigned sendPubKeyDataLen,		// 0 if sendPubKeyData is NULL
  	const unsigned char *otherKeyData,	// optional
  	unsigned otherKeyDataLen,		// 0 if otherKeyData is NULL
  	const unsigned char *sigData,	// optional; NULL means no signature
  	unsigned sigDataLen,		// 0 if sigData is NULL
  	unsigned userData)		// for caller's convenience
  {
  	cFileInst *cfinst;
  	cFileHeader *header;
  	unsigned char *data;
  
  	if(cipherTextLen == 0) {
  		return NULL;
  	}
  	cfinst = cFileInstAlloc();
  	header = &cfinst->header;
  
  	/*
  	 * Init the header.
  	 */
  	header->magic 		  = CFILE_MAGIC;
  	header->version 	  = CFILE_VERSION;
  	header->minVersion	  = CFILE_MIN_VERSION;
  	header->totalLength	  = lengthOfByteRepCfileHdr() + cipherTextLen +
  				    sendPubKeyDataLen + otherKeyDataLen +
  				    sigDataLen;
  	header->encrType	     = encrType;
  	header->cipherTextOffset     = lengthOfByteRepCfileHdr();
  	header->cipherTextLen        = cipherTextLen;
  	header->sendPubKeyDataOffset = header->cipherTextOffset +
  				       cipherTextLen;
  	header->sendPubKeyDataLen    = sendPubKeyDataLen;
  	header->otherKeyDataOffset   = header->sendPubKeyDataOffset +
  				       sendPubKeyDataLen;
  	header->otherKeyDataLen      = otherKeyDataLen;
  	header->sigDataOffset	     = header->otherKeyDataOffset +
  				       otherKeyDataLen;
  	header->sigDataLen	     = sigDataLen;
  	header->userData	     = userData;
  
  	/*
  	 * Alloc a data representation, copy various components to it.
  	 */
  	cfinst->dataRepLen = header->totalLength;
  	data = cfinst->dataRep = (unsigned char*) fmalloc(cfinst->dataRepLen);
  	cfileHdrToByteRep(header, data);
  
  	data = cfinst->dataRep + header->cipherTextOffset;
  	bcopy(cipherText, data, cipherTextLen);
  	if(sendPubKeyDataLen) {
  		data = cfinst->dataRep + header->sendPubKeyDataOffset;
  		bcopy(sendPubKeyData, data, sendPubKeyDataLen);
  	}
  	if(otherKeyDataLen) {
  		data = cfinst->dataRep + header->otherKeyDataOffset;
  		bcopy(otherKeyData, data, otherKeyDataLen);
  	}
  	if(sigDataLen) {
  		data = cfinst->dataRep + header->sigDataOffset;
  		bcopy(sigData, data, sigDataLen);
  	}
  	return (feeCipherFile)cfinst;
  }
  
  /*
   * Obtain the contents of a feeCipherFile as a byte stream.
   */
  feeReturn feeCFileDataRepresentation(feeCipherFile cipherFile,
  	const unsigned char **dataRep,
  	unsigned *dataRepLen)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	if(cfinst->dataRepLen == 0) {
  		*dataRep = NULL;
  		*dataRepLen = 0;
  		return FR_BadCipherFile;
  	}
  	*dataRep = (unsigned char*) fmallocWithData(cfinst->dataRep, cfinst->dataRepLen);
  	*dataRepLen = cfinst->dataRepLen;
  	return FR_Success;
  }
  
  /*
   * Alloc and return a new feeCipherFile object, given a byte stream (originally
   * obtained from feeCFDataRepresentation()).
   */
  feeReturn feeCFileNewFromDataRep(const unsigned char *dataRep,
  	unsigned dataRepLen,
  	feeCipherFile *cipherFile)	// RETURNED if sucessful
  {
  	cFileInst *cfinst = cFileInstAlloc();
  	cFileHeader *header;
  
  	if(dataRepLen < lengthOfByteRepCfileHdr()) {
  		dbgLog(("datRep too short\n"));
  		goto abort;
  	}
  	cfinst->dataRep = (unsigned char*) fmallocWithData(dataRep, dataRepLen);
  	cfinst->dataRepLen = dataRepLen;
  	header = &cfinst->header;
  	byteRepToCfileHdr(dataRep, header);
  
  	/*
  	 * As much consistency checking as we can manage here.
  	 */
  	if(header->magic != CFILE_MAGIC) {
  		dbgLog(("Bad cipherFile magic number\n"));
  		goto abort;
  	}
  	if(header->minVersion > CFILE_VERSION) {
  		dbgLog(("Incompatible cipherFile version\n"));
  		goto abort;
  	}
  	if(header->totalLength != dataRepLen) {
  		dbgLog(("Bad totalLength in cipherFile header\n"));
  		goto abort;
  	}
  	if(((header->cipherTextOffset + header->cipherTextLen) >
  			header->totalLength) ||
  	   ((header->sendPubKeyDataOffset + header->sendPubKeyDataLen) >
  			header->totalLength) ||
  	   ((header->otherKeyDataOffset + header->otherKeyDataLen) >
  			header->totalLength) ||
  	   ((header->sigDataOffset  + header->sigDataLen) >
  			header->totalLength)) {
  		dbgLog(("Bad element lengths in cipherFile header\n"));
  		goto abort;
  	}
  
  	/*
  	 * OK, looks good.
  	 */
  	*cipherFile = (feeCipherFile)cfinst;
  	return FR_Success;
  abort:
  	cFileInstFree(cfinst);
  	*cipherFile = NULL;
  	return FR_BadCipherFile;
  }
  
  /*
   * Free a feeCipherFile object.
   */
  void feeCFileFree(feeCipherFile cipherFile)
  {
  	cFileInstFree((cFileInst *)cipherFile);
  }
  
  /*
   * Given a feeCipherFile object (typically obtained from
   * feeCFileNewFromData()), obtain its constituent parts.
   *
   * Data returned must be freed by caller.
   * feeCFileSigData() may return NULL, indicating no signature present.
   */
  cipherFileEncrType feeCFileEncrType(feeCipherFile cipherFile)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	return cfinst->header.encrType;
  }
  
  unsigned char *feeCFileCipherText(feeCipherFile cipherFile,
  	unsigned *cipherTextLen)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	if(cfinst->header.cipherTextLen) {
  		*cipherTextLen = cfinst->header.cipherTextLen;
  		return (unsigned char*) fmallocWithData(cfinst->dataRep +
  			cfinst->header.cipherTextOffset, *cipherTextLen);
  	}
  	else {
  		dbgLog(("feeCFileCipherText: no cipherText\n"));
  		*cipherTextLen = 0;
  		return NULL;
  	}
  }
  
  unsigned char *feeCFileSendPubKeyData(feeCipherFile cipherFile,
  	unsigned *sendPubKeyDataLen)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	if(cfinst->header.sendPubKeyDataLen) {
  		*sendPubKeyDataLen = cfinst->header.sendPubKeyDataLen;
  		return (unsigned char*) fmallocWithData(cfinst->dataRep +
  			cfinst->header.sendPubKeyDataOffset,
  			*sendPubKeyDataLen);
  	}
  	else {
  		*sendPubKeyDataLen = 0;
  		return NULL;
  	}
  }
  
  unsigned char *feeCFileOtherKeyData(feeCipherFile cipherFile,
  	unsigned *otherKeyDataLen)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	if(cfinst->header.otherKeyDataLen) {
  		*otherKeyDataLen = cfinst->header.otherKeyDataLen;
  		return (unsigned char*) fmallocWithData(cfinst->dataRep +
  			cfinst->header.otherKeyDataOffset, *otherKeyDataLen);
  	}
  	else {
  		*otherKeyDataLen = 0;
  		return NULL;
  	}
  }
  
  unsigned char *feeCFileSigData(feeCipherFile cipherFile,
  	unsigned *sigDataLen)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	if(cfinst->header.sigDataLen) {
  		*sigDataLen = cfinst->header.sigDataLen;
  		return (unsigned char*) fmallocWithData(cfinst->dataRep +
  			cfinst->header.sigDataOffset, *sigDataLen);
  	}
  	else {
  		/*
  		 * Not an error
  		 */
  		*sigDataLen = 0;
  		return NULL;
  	}
  }
  
  unsigned feeCFileUserData(feeCipherFile cipherFile)
  {
  	cFileInst *cfinst = (cFileInst *)cipherFile;
  
  	return cfinst->header.userData;
  }
  
  /*
   * Convert between cFileHeader and portable byte representation.
   */
  
  /*
   * Return size of byte rep of cFileHeader. We just happen to know that
   * this is the same size as the header....
   */
  static unsigned lengthOfByteRepCfileHdr(void)
  {
  	return sizeof(cFileHeader);
  }
  
  static unsigned cfileHdrToByteRep(cFileHeader *hdr,
  	unsigned char *s)
  {
  	s += intToByteRep(hdr->magic, s);
  	s += intToByteRep(hdr->version, s);
  	s += intToByteRep(hdr->minVersion, s);
  	s += intToByteRep(hdr->totalLength, s);
  	s += intToByteRep(hdr->encrType, s);
  	s += intToByteRep(hdr->cipherTextOffset, s);
  	s += intToByteRep(hdr->cipherTextLen, s);
  	s += intToByteRep(hdr->sendPubKeyDataOffset, s);
  	s += intToByteRep(hdr->sendPubKeyDataLen, s);
  	s += intToByteRep(hdr->otherKeyDataOffset, s);
  	s += intToByteRep(hdr->otherKeyDataLen, s);
  	s += intToByteRep(hdr->sigDataOffset, s);
  	s += intToByteRep(hdr->sigDataLen, s);
  	s += intToByteRep(hdr->userData, s);
  	return sizeof(cFileHeader);
  }
  
  #define DEC_INT(n, b)		\
  	n = byteRepToInt(b);	\
  	b += sizeof(int);
  
  static void byteRepToCfileHdr(const unsigned char *s,
  	cFileHeader *hdr)
  {
  	DEC_INT(hdr->magic, s);
  	DEC_INT(hdr->version, s);
  	DEC_INT(hdr->minVersion, s);
  	DEC_INT(hdr->totalLength, s);
  //	DEC_INT(hdr->encrType, s);
  	hdr->encrType = (cipherFileEncrType) byteRepToInt(s);
  	s += sizeof(int);
  	DEC_INT(hdr->cipherTextOffset, s);
  	DEC_INT(hdr->cipherTextLen, s);
  	DEC_INT(hdr->sendPubKeyDataOffset, s);
  	DEC_INT(hdr->sendPubKeyDataLen, s);
  	DEC_INT(hdr->otherKeyDataOffset, s);
  	DEC_INT(hdr->otherKeyDataLen, s);
  	DEC_INT(hdr->sigDataOffset, s);
  	DEC_INT(hdr->sigDataLen, s);
  	DEC_INT(hdr->userData, s);
  }
  