/*
 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Yokogawa Electric Corporation,
 * YDC Corporation, IPA (Information-technology Promotion Agency, Japan).
 * All rights reserved.
 * 
 * Redistribution and use of this software in source and binary forms, with 
 * or without modification, are permitted provided that the following 
 * conditions and disclaimer are agreed and accepted by the user:
 * 
 * 1. Redistributions of source code must retain the above copyright 
 * notice, this list of conditions and the following disclaimer.
 * 
 * 2. 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.
 * 
 * 3. Neither the names of the copyrighters, the name of the project which 
 * is related to this software (hereinafter referred to as "project") nor 
 * the names of the contributors may be used to endorse or promote products 
 * derived from this software without specific prior written permission.
 * 
 * 4. No merchantable use may be permitted without prior written 
 * notification to the copyrighters. However, using this software for the 
 * purpose of testing or evaluating any products including merchantable 
 * products may be permitted without any notification to the copyrighters.
 * 
 * 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHTERS, THE PROJECT AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING 
 * BUT NOT LIMITED THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.  IN NO EVENT SHALL THE 
 * COPYRIGHTERS, THE PROJECT 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.
 *
 * $TAHI: v6eval/lib/Pz/MfAlgorithm.cc,v 1.29 2005/05/09 09:35:24 akisada Exp $
 */
#include "MfAlgorithm.h"
#include "PAlgorithm.h"
#include "PvOctets.h"
#include "CmMain.h"
#include "sslconfig.h"
#include <string.h>

#include "debug.h"

//----------------------------------------------------------------------
OCTBUF* MfESPPad::Padding_Octets(uint32_t len) const {
	return padding_->substr(0,len);}

OCTBUF* MfESPPad::defaultOctets(uint32_t len) {
	MfESPPad* m=defaultPadding();
	return m!=0?m->Padding_Octets(len):new PvOctets(len);}

OCTBUF *
MfESPPad::IKEdefaultOctets(uint32_t len)
{
	MfESPPad *m = IKEdefaultPadding();
	return(m? m->Padding_Octets(len): new PvOctets(len));
}

//----------------------------------------------------------------------
// NULL AUTHENTIFICATION
OCTSTR MfAuth::init(OCTSTR,const PObjectList&) const {return 0;}
void MfAuth::update(OCTSTR,const PObjectList&,const OCTBUF&) const {}
PvOctets* MfAuth::result(OCTSTR,const PObjectList&) const {return 0;}
uint32_t MfAuth::alignment(const PObjectList&) const {return alignUnit();}

//----------------------------------------------------------------------
// NULL CRYPT ALGORITHM
uint32_t MfCrypt::alignment(const PObjectList& args) const {
	uint32_t n=alignTimes(args);
	uint8_t u=alignUnit();
	return n>1?n*u:u;}

bool MfCrypt::alignmentCheck(uint32_t l,const PObjectList& args) const {
	uint8_t reqAlign=alignUnit();
	if(l%reqAlign!=0) {
		printf("log: length %d not match with required alignment %d on %s\n",
			l,reqAlign,string());
		return false;}
	uint32_t funcAlign=alignment(args);
	if(l%funcAlign!=0) {
		printf("log: length %d not match with function alignment %d on %s\n",
			l,funcAlign,string());}
	return true;}

//----------------------------------------------------------------------
uint32_t MfCrypt::alignTimes(const PObjectList& args) const {
	uint32_t n=1;
	if(args.size()>0) {
		bool ok=true;
		n=args[0]->intValue(ok);
		if(!ok) n=1;}
	return n>1?n:1;}

uint32_t MfCryptKey::alignTimes(const PObjectList& args) const {
	uint32_t n=1;
	if(args.size()>2) {
		bool ok=true;
		n=args[2]->intValue(ok);
		if(!ok) n=1;}
	return n>1?n:1;}

//----------------------------------------------------------------------
const PObject* MfCrypt::key(const PObjectList&) const {
	return 0;}

const PObject* MfCryptKey::key(const PObjectList& args) const {
	return (args.size()>0)?args[0]:0;}

//----------------------------------------------------------------------
void MfCrypt::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject*,OCTSTR) const {
	memcpy(os,is,l);}
void MfCrypt::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject*,OCTSTR) const {
	memcpy(os,is,l);}

//----------------------------------------------------------------------
OCTBUF* MfCrypt::encryptOctets(const OCTBUF& text,const PObjectList& a,OCTBUF* ivec) const {
	if(DBGFLAGS('E')) {printf("===text\n"); text.dump(); printf("\n");}
	//--------------------------------------------------------------
	// length check
	uint32_t lenc=text.length();
	//--------------------------------------------------------------
	// initial vector
	// if no ivec specified in function, it's depend on malloc
	// how can i return random value?
	// should i make it error?
	OCTSTR ivp=0;
	if(a.size()>1) {
		bool ok=true;
		ivp=(OCTSTR)a[1]->octetsValue(ok);}
	uint32_t ivlen=ivecLength();
	if(ivec!=0) {ivec->set(ivlen,ivp);}
	PvOctets ivwork(ivlen,ivp,true);	// IV field changes
	PvOctets* enc=new PvOctets(ivlen+lenc);
	OCTSTR os=enc->buffer();
	OCTSTR is=(OCTSTR)text.string();
	const PObject* k=key(a);
	OCTSTR wp=ivwork.buffer();
	memcpy(os,wp,ivlen); os+=ivlen;		// initial vector
	if(!alignmentCheck(lenc,a)) {
		MfCrypt::encrypt(os,is,lenc,k,wp);}
	else {
		encrypt(os,is,lenc,k,wp);}
	if(DBGFLAGS('E')) {printf("===encrypt\n"); enc->dump(); printf("\n");}
	return enc;}

OCTBUF* MfCrypt::decryptOctets(const OCTBUF& cipher,const PObjectList& a,
		OCTBUF*& ivec) const {
	if(DBGFLAGS('D')) {printf("===cipher\n"); cipher.dump(); printf("\n");}
	//--------------------------------------------------------------
	// length check: use super class decrypt
	uint32_t lcipher=cipher.length();
	uint32_t ivlen=ivecLength();
	uint32_t ldec=lcipher-ivlen;
	//--------------------------------------------------------------
	// initial vector
	OCTSTR cp=(OCTSTR)cipher.string();
	if(ivlen>0) {
		if(ivec==0) {ivec=new PvOctets(0);}
		ivec->set(ivlen,cp);}
	PvOctets ivwork(ivlen,cp,true);		// IV field changes
	PvOctets* dec=new PvOctets(ldec);
	OCTSTR os=dec->buffer();
	OCTSTR is=cp+ivlen;
	const PObject* k=key(a);
	OCTSTR wp=ivwork.buffer();
	if(!alignmentCheck(ldec,a)) {
		MfCrypt::decrypt(os,is,ldec,k,wp);}
	else {
		decrypt(os,is,ldec,k,wp);}
	if(DBGFLAGS('D')) {printf("===decrypt\n"); dec->dump(); printf("\n");}
	return dec;}

#include <openssl/des.h>
//----------------------------------------------------------------------
// DESCBC CRYPT ALGORITHM
void MfDESCBC::scheduleKeys(const PObject* key,des_key_schedule& schd) const {
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);
	des_key_sched((DES_CBLOCK_IN)keys,schd);}

void MfDESCBC::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	des_key_schedule schd;
	scheduleKeys(key,schd);
	des_cbc_encrypt(is,os,l,schd,(DES_CBLOCK_IO)iv,DES_ENCRYPT);}

void MfDESCBC::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	des_key_schedule schd;
	scheduleKeys(key,schd);
	des_cbc_encrypt(is,os,l,schd,(DES_CBLOCK_IO)iv,DES_DECRYPT);}

#include <openssl/blowfish.h>
//----------------------------------------------------------------------
// BLOWFISH CRYPT ALGORITHM
void MfBLOWFISH::scheduleKeys(const PObject* key,BF_KEY& schd) const {
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);
	BF_set_key(&schd,key->length(),(OCTSTR)keys);}

void MfBLOWFISH::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	BF_KEY schd;
	scheduleKeys(key,schd);
	BF_cbc_encrypt(is,os,l,&schd,iv,DES_ENCRYPT);}

void MfBLOWFISH::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	BF_KEY schd;
	scheduleKeys(key,schd);
	BF_cbc_encrypt(is,os,l,&schd,iv,DES_DECRYPT);}

#include <openssl/rc5.h>
//----------------------------------------------------------------------
// RC5 CRYPT ALGORITHM
void MfRC5::scheduleKeys(const PObject* key,RC5_32_KEY& schd) const {
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);
	RC5_32_set_key(&schd,key->length(),(OCTSTR)keys,RC5_16_ROUNDS);}

void MfRC5::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	RC5_32_KEY schd;
	scheduleKeys(key,schd);
	RC5_32_cbc_encrypt(is,os,l,&schd,iv,DES_ENCRYPT);}

void MfRC5::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	RC5_32_KEY schd;
	scheduleKeys(key,schd);
	RC5_32_cbc_encrypt(is,os,l,&schd,iv,DES_DECRYPT);}

#include <openssl/cast.h>
//----------------------------------------------------------------------
// CAST128 CRYPT ALGORITHM
void MfCAST128::scheduleKeys(const PObject* key,CAST_KEY& schd) const {
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);
	CAST_set_key(&schd,key->length(),(OCTSTR)keys);}

void MfCAST128::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	CAST_KEY schd;
	scheduleKeys(key,schd);
	CAST_cbc_encrypt(is,os,l,&schd,iv,DES_ENCRYPT);}

void MfCAST128::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	CAST_KEY schd;
	scheduleKeys(key,schd);
	CAST_cbc_encrypt(is,os,l,&schd,iv,DES_DECRYPT);}

//----------------------------------------------------------------------
// DES3CBC CRYPT ALGORITHM
void MfDES3CBC::scheduleKeys(const PObject* key,des_key_schedule& schd0,
		des_key_schedule& schd1,des_key_schedule& schd2) const{
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);
	des_key_sched((DES_CBLOCK_IN)keys,schd0);
	des_key_sched((DES_CBLOCK_IN)(keys+8),schd1);
	des_key_sched((DES_CBLOCK_IN)(keys+16),schd2);}

void MfDES3CBC::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	des_key_schedule schd0,schd1,schd2;
	scheduleKeys(key,schd0,schd1,schd2);
	des_ede3_cbc_encrypt(is,os,l,schd0,schd1,schd2,(DES_CBLOCK_IO)iv,DES_ENCRYPT);}

void MfDES3CBC::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	des_key_schedule schd0,schd1,schd2;
	scheduleKeys(key,schd0,schd1,schd2);
	des_ede3_cbc_encrypt(is,os,l,schd0,schd1,schd2,(DES_CBLOCK_IO)iv,DES_DECRYPT);}

extern "C" {
#include <crypto/rijndael/rijndael.h>
}
//----------------------------------------------------------------------
// RIJNDAEL/AES CBC CRYPT ALGORITHM
void MfRIJNDAEL::encrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	keyInstance k;
	cipherInstance c;
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);

	rijndael_makeKey(&k, DIR_ENCRYPT, key->length() << 3, (char *)keys);
	rijndael_cipherInit(&c, MODE_CBC, (char *)iv);
	rijndael_blockEncrypt(&c, &k, is, l << 3, os);}

void MfRIJNDAEL::decrypt(OCTSTR os,OCTSTR is,uint32_t l,const PObject* key,OCTSTR iv) const {
	keyInstance k;
	cipherInstance c;
	bool ok=true;
	COCTSTR keys=key->octetsValue(ok);

	rijndael_makeKey(&k, DIR_DECRYPT, key->length() << 3, (char *)keys);
	rijndael_cipherInit(&c, MODE_CBC, (char *)iv);
	rijndael_blockDecrypt(&c, &k, is, l << 3, os);}

#include <openssl/hmac.h>
//----------------------------------------------------------------------
// HMAC AUTHENTIFICATION ALGORITHM
OCTSTR MfHMAC::init(OCTSTR cp,const PObjectList& a) const {
	HMAC_CTX* ctx=cp!=0?(HMAC_CTX*)cp:new HMAC_CTX;
	bool ok=true;
	COCTSTR key=0;
	uint32_t keylen=0;
	if(a.size()>0) {
		key=a[0]->octetsValue(ok);
		keylen=a[0]->length();}
	HMAC_Init(ctx,(OCTSTR)key,keylen,evp());
	return (OCTSTR)ctx;}

void MfHMAC::update(OCTSTR cp,const PObjectList&,const OCTBUF& s) const {
	HMAC_CTX* ctx=(HMAC_CTX*)cp;
	HMAC_Update(ctx,(OCTSTR)s.string(),s.length());}

PvOctets* MfHMAC::result(OCTSTR cp,const PObjectList&) const {
	HMAC_CTX* ctx=(HMAC_CTX*)cp;
	uint32_t len=HMAC_MAX_MD_CBLOCK;
	octet m[HMAC_MAX_MD_CBLOCK];
	HMAC_Final(ctx,m,&len);
	uint32_t icvlen=icvLength();
	PvOctets* rc=new PvOctets(icvlen);
	OCTSTR os=rc->buffer();
	memcpy(os,m,icvlen);
	HMAC_cleanup(ctx);
	return rc;}

#include <crypto/sha2/sha2.h>
//----------------------------------------------------------------------
// EVP ALGORITHM
const EVP_MD* MfHMAC::evp() const {return 0;}
const EVP_MD* MfHMACMD5::evp() const {return EVP_md5();}
const EVP_MD* MfHMACSHA1::evp() const {return EVP_sha1();}
EVP_MD* MfHMACSHA2_256::evp() const {return EVP_sha2_256();}
EVP_MD* MfHMACSHA2_384::evp() const {return EVP_sha2_384();}
EVP_MD* MfHMACSHA2_512::evp() const {return EVP_sha2_512();}

//----------------------------------------------------------------------
bool MfESPPad::checkArgument(const PFunction&,const PObjectList&) const {
	return true;}
bool MfAuth::checkArgument(const PFunction&,const PObjectList&) const {
	return true;}

//----------------------------------------------------------------------
// xxx([key])
bool MfHMAC::checkArgument(const PFunction& o,const PObjectList& a) const {
	bool ok=true;
	bool rc=true;
	uint32_t n=a.size();
	CSTR name=o.metaString();
	if(n!=0&&n!=1) {
		o.error("E %s must have 0 or 1 argument, not %d",name,n);
		return false;}
	if(n==0) {return true;}
	a[0]->octetsValue(ok);
	if(!ok) {
		o.error("E %s first argument has to be octets",name);
		rc=false;}
	return rc;}

//----------------------------------------------------------------------
// null([alignment])
bool MfCrypt::checkArgument(const PFunction& o,const PObjectList& a) const {
	bool ok=true;
	bool rc=true;
	uint32_t n=a.size();
	CSTR name=o.metaString();
	if(n!=0&&n!=1) {
		o.error("E %s must have 0 or 1 argument, not %d",name,n);
		return false;}
	if(n==0) {return true;}
	a[0]->intValue(ok);
	if(!ok) {
		o.error("E %s first argument has to be int",name);
		rc=false;}
	return rc;}

//----------------------------------------------------------------------
// xxx(key[,ivec[,alignment]])
bool MfCryptKey::checkArgument(const PFunction& o,const PObjectList& a) const {
	bool ok=true;
	bool rc=true;
	uint32_t n=a.size();
	CSTR name=o.metaString();
	if(n!=1&&n!=2&&n!=3) {
		o.error("E %s mast have 1-3 arguments, not %d",name,n);
		return false;}
	a[0]->octetsValue(ok);
	if(!ok) {
		o.error("E %s first argument has to be octets",name);
		rc=false;}
	else {
		uint32_t l=a[0]->length();
		uint32_t kl=keyLength();
		if(l!=kl) {
			o.error("E %s key length has to be %d, not %d",name,kl,l);
			rc=false;}}
	if(n>1) {
		a[1]->octetsValue(ok);
		if(!ok) {
			o.error("E %s second argument has to be octets",name);
			rc=false;}
		else {
			uint32_t l=a[1]->length();
			uint32_t il=ivecLength();
			if(l!=il) {
				o.error("E %s ivec length has to be %d, not %d",name,il,l);
				rc=false;}}}
	if(n==3) {
		uint32_t t=a[2]->intValue(ok);
		if(!ok) {
			o.error("E %s third argument has to be int",name);
			rc=false;}
		uint32_t l=t*alignUnit();
		if(l>256) {
			o.error("E %s alignment %d is too big",name,t);
			rc=false;}}
	return rc;}

//----------------------------------------------------------------------
PObject* MfESPPad::tokenObject(int l,CSTR f) const {
	return new PfESPPad(this,f,l);}
PObject* MfCrypt::tokenObject(int l,CSTR f) const {
	return new PfCrypt(this,f,l);}
PObject* MfAuth::tokenObject(int l,CSTR f) const {
	return new PfAuth(this,f,l);}

//----------------------------------------------------------------------
// CONSTRUCTOR/DESTRUCTOR
MfESPPad::MfESPPad(CSTR s,PvOctets* o):MvFunction(s),padding_(o) {}
MfESPPad::~MfESPPad() {}
MfCrypt::MfCrypt(CSTR s,uint8_t k,uint8_t i,uint8_t a):MvFunction(s),
	keyLength_(k),ivecLength_(i),alignUnit_(a),dummy_(0) {}
MfCrypt::~MfCrypt() {}
MfESPPadAny::MfESPPadAny(CSTR s,PvOctets* o):MfESPPad(s,o) {}
MfESPPadAny::~MfESPPadAny() {}
MfCryptKey::MfCryptKey(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCrypt(s,k,i,a) {}
MfCryptKey::~MfCryptKey() {}
MfDESCBC::MfDESCBC(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfDESCBC::~MfDESCBC() {}
MfBLOWFISH::MfBLOWFISH(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfBLOWFISH::~MfBLOWFISH() {}
MfRC5::MfRC5(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfRC5::~MfRC5() {}
MfCAST128::MfCAST128(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfCAST128::~MfCAST128() {}
MfDES3CBC::MfDES3CBC(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfDES3CBC::~MfDES3CBC() {}

MfIKE_DESCBC::MfIKE_DESCBC(CSTR s, uint8_t k, uint8_t i, uint8_t a):
	MfDESCBC(s, k, i, a) {}
MfIKE_DESCBC::~MfIKE_DESCBC() {}

MfIKE_DES3CBC::MfIKE_DES3CBC(CSTR s, uint8_t k, uint8_t i, uint8_t a):
	MfDES3CBC(s, k, i, a) {}
MfIKE_DES3CBC::~MfIKE_DES3CBC() {}

MfDESCBC_2::MfDESCBC_2(CSTR s, uint8_t k, uint8_t i, uint8_t a):
	MfIKE_DESCBC(s, k, i, a) {}
MfDESCBC_2::~MfDESCBC_2() {}

MfDES3CBC_2::MfDES3CBC_2(CSTR s, uint8_t k, uint8_t i, uint8_t a):
	MfIKE_DES3CBC(s, k, i, a) {}
MfDES3CBC_2::~MfDES3CBC_2() {}

bool
MfIKE_DESCBC::checkArgument(const PFunction &o, const PObjectList &a) const
{
	bool ok = true;
	bool rc = true;
	uint32_t n = a.size();
	CSTR name = o.metaString();

	if((n != 1) && (n != 2) && (n != 3)) {
		o.error("E %s mast have 1-3 arguments, not %d", name, n);
		return(false);
	}

	if(!a[0]->isOctets()) {
		o.error("E %s first argument has to be octet stream", name);
		rc = false;
	}

	if(n > 1) {
		if(!a[0]->isOctets()) {
			o.error("E %s second argument has to be octet stream",
				name);

			rc = false;
		}
	}

	if(n == 3) {
		uint32_t t = a[2]->intValue(ok);

		if(!ok) {
			o.error("E %s third argument has to be int", name);
			rc = false;
		}

		uint32_t l = t * alignUnit();

		if(l > 256) {
			o.error("E %s alignment %d is too big", name, t);
			rc = false;
		}
	}

	return(rc);
}

bool
MfIKE_DES3CBC::checkArgument(const PFunction &o, const PObjectList &a) const
{
	bool ok = true;
	bool rc = true;
	uint32_t n = a.size();
	CSTR name = o.metaString();

	if((n != 1) && (n != 2) && (n != 3)) {
		o.error("E %s mast have 1-3 arguments, not %d", name, n);
		return(false);
	}

	if(!a[0]->isOctets()) {
		o.error("E %s first argument has to be octet stream", name);
		rc = false;
	}

	if(n > 1) {
		if(!a[0]->isOctets()) {
			o.error("E %s second argument has to be octet stream",
				name);

			rc = false;
		}
	}

	if(n == 3) {
		uint32_t t = a[2]->intValue(ok);

		if(!ok) {
			o.error("E %s third argument has to be int", name);
			rc = false;
		}

		uint32_t l = t * alignUnit();

		if(l > 256) {
			o.error("E %s alignment %d is too big", name, t);
			rc = false;
		}
	}

	return(rc);
}

OCTBUF *
MfIKE_DESCBC::encryptOctets(const OCTBUF &text,
	const PObjectList &a, OCTBUF *ivec) const
{
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "plaintext",
	text.string(), text.length());
#endif	// ISAKMP_DBG
	if(DBGFLAGS('E')) {
		printf("===text\n");
		text.dump();
		printf("\n");
	}

	uint32_t lenc = text.length();

	OCTSTR ivp = 0;
	uint32_t ivlen = ivecLength();

	PvOctets isakmp_ivec;

	if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(isakmp_ivec, 0)) {
			ivp = isakmp_ivec.buffer();
			ivlen = isakmp_ivec.length();
		}
	}

	if(ivec != 0) {
		ivec->set(ivlen, ivp);
	}

	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *enc = new PvOctets(lenc);

	OCTSTR os = enc->buffer();
	OCTSTR is = (OCTSTR)text.string();

	PObject *p0 = a[0];
	PvOctets isakmp_key;
	p0->generateOctetsWith(isakmp_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "key",
	isakmp_key.buffer(), isakmp_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &isakmp_key;

	OCTSTR wp = ivwork.buffer();

	if(!alignmentCheck(lenc, a)) {
		MfCrypt::encrypt(os, is, lenc, k, wp);
	} else {
		encrypt(os, is, lenc, k, wp);
	}

	if(DBGFLAGS('E')) {
		printf("===encrypt\n");
		enc->dump();
		printf("\n");
	}

	return(enc);
}

OCTBUF *
MfIKE_DESCBC::decryptOctets(const OCTBUF &cipher,
	const PObjectList &a, OCTBUF *&ivec) const
{
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "ciphertext",
	cipher.string(), cipher.length());
#endif	// ISAKMP_DBG
	if(DBGFLAGS('D')) {
		printf("===cipher\n");
		cipher.dump();
		printf("\n");
	}

	uint32_t lcipher	= cipher.length();
	uint32_t ivlen		= ivecLength();
	uint32_t ldec		= lcipher;

	OCTSTR cp		= (OCTSTR)cipher.string();

	OCTSTR ivp = 0;
	PvOctets isakmp_ivec;

	if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(isakmp_ivec, 0)) {
			ivp = isakmp_ivec.buffer();
			ivlen = isakmp_ivec.length();
		}
	}

	if(ivlen > 0) {
		if(!ivec) {
			ivec = new PvOctets(0);
		}

		ivec->set(ivlen, ivp);
	}

	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *dec = new PvOctets(ldec);

	OCTSTR os = dec->buffer();
	OCTSTR is = cp;

	PObject *p0 = a[0];
	PvOctets isakmp_key;
	p0->generateOctetsWith(isakmp_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DESCBC", "key",
	isakmp_key.buffer(), isakmp_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &isakmp_key;
	OCTSTR wp = ivwork.buffer();

	if(!alignmentCheck(ldec,a)) {
		MfCrypt::decrypt(os, is, ldec, k, wp);
	} else {
		decrypt(os, is, ldec, k, wp);
	}

	if(DBGFLAGS('D')) {
		printf("===decrypt\n");
		dec->dump();
		printf("\n");
	}

	return(dec);
}

OCTBUF *
MfIKE_DES3CBC::encryptOctets(const OCTBUF &text,
	const PObjectList &a, OCTBUF *ivec) const
{
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "plaintext",
	text.string(), text.length());
#endif	// ISAKMP_DBG
	if(DBGFLAGS('E')) {
		printf("===text\n");
		text.dump();
		printf("\n");
	}

	uint32_t lenc = text.length();

	OCTSTR ivp = 0;
	uint32_t ivlen = ivecLength();
#ifdef ISAKMP_DBG
	dbg("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC",
		"a.size(): %d\n", a.size());
#endif  // ISAKMP_DBG
	PvOctets isakmp_ivec;

	if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(isakmp_ivec, 0)) {
			ivp = isakmp_ivec.buffer();
			ivlen = isakmp_ivec.length();
		}

#ifdef ISAKMP_DBG
	dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "isakmp_ivec[right]",
		isakmp_ivec.buffer(), isakmp_ivec.length());
	dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "isakmp_ivec[left]",
		ivp, ivlen);
#endif	// ISAKMP_DBG
	}
#ifdef ISAKMP_DBG
	dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "isakmp_ivec[out:0]",
		ivp, ivlen);
#endif	// ISAKMP_DBG
	if(ivec) {
		ivec->set(ivlen, ivp);
	}
#ifdef ISAKMP_DBG
	dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "isakmp_ivec[out:1]",
		ivp, ivlen);
#endif	// ISAKMP_DBG
	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *enc = new PvOctets(lenc);

	OCTSTR os = enc->buffer();
	OCTSTR is = (OCTSTR)text.string();

	PObject *p0 = a[0];
	PvOctets isakmp_key;
	p0->generateOctetsWith(isakmp_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "key",
	isakmp_key.buffer(), isakmp_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &isakmp_key;

	OCTSTR wp = ivwork.buffer();

	if(!alignmentCheck(lenc, a)) {
		MfCrypt::encrypt(os, is, lenc, k, wp);
	} else {
		encrypt(os, is, lenc, k, wp);
	}

	if(DBGFLAGS('E')) {
		printf("===encrypt\n");
		enc->dump();
		printf("\n");
	}

	return(enc);
}

OCTBUF *
MfIKE_DES3CBC::decryptOctets(const OCTBUF &cipher,
	const PObjectList &a, OCTBUF *&ivec) const
{
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "ciphertext",
	cipher.string(), cipher.length());
#endif	// ISAKMP_DBG
	if(DBGFLAGS('D')) {
		printf("===cipher\n");
		cipher.dump();
		printf("\n");
	}

	uint32_t lcipher	= cipher.length();
	uint32_t ivlen		= ivecLength();
	uint32_t ldec		= lcipher;

	OCTSTR cp		= (OCTSTR)cipher.string();

	OCTSTR ivp = 0;
	PvOctets isakmp_ivec;

	if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(isakmp_ivec, 0)) {
			ivp = isakmp_ivec.buffer();
			ivlen = isakmp_ivec.length();
		}
	}

	if(ivlen > 0) {
		if(!ivec) {
			ivec = new PvOctets(0);
		}

		ivec->set(ivlen, ivp);
	}

	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *dec = new PvOctets(ldec);

	OCTSTR os = dec->buffer();
	OCTSTR is = cp;

	PObject *p0 = a[0];
	PvOctets isakmp_key;
	p0->generateOctetsWith(isakmp_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfIKE_DES3CBC", "key",
	isakmp_key.buffer(), isakmp_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &isakmp_key;

	OCTSTR wp = ivwork.buffer();

	if(!alignmentCheck(ldec,a)) {
		MfCrypt::decrypt(os, is, ldec, k, wp);
	} else {
		decrypt(os, is, ldec, k, wp);
	}

	if(DBGFLAGS('D')) {
		printf("===decrypt\n");
		dec->dump();
		printf("\n");
	}

	return(dec);
}

OCTBUF *
MfDESCBC_2::encryptOctets(const OCTBUF &text,
	const PObjectList &a, OCTBUF *ivec) const
{
	if(DBGFLAGS('E')) {
		printf("===text\n");
		text.dump();
		printf("\n");
	}

        uint32_t lenc = text.length();

	OCTSTR ivp = 0;
	PvOctets ipsec_ivec;

        if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(ipsec_ivec, 0)) {
			ivp = ipsec_ivec.buffer();
		}
	}

	uint32_t ivlen = ivecLength();
	if(ivec) {
		ivec->set(ivlen, ivp);
	}

	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDESCBC_2", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *enc = new PvOctets(ivlen + lenc);
	OCTSTR os = enc->buffer();
	OCTSTR is = (OCTSTR)text.string();

	PObject *p0 = a[0];
	PvOctets ipsec_key;
	p0->generateOctetsWith(ipsec_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDESCBC_2", "key",
	ipsec_key.buffer(), ipsec_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &ipsec_key;

	OCTSTR wp = ivwork.buffer();
	memcpy(os, wp, ivlen);
	os += ivlen;

	if(!alignmentCheck(lenc, a)) {
		MfCrypt::encrypt(os, is, lenc, k, wp);
	} else {
		encrypt(os, is, lenc, k, wp);
	}

	if(DBGFLAGS('E')) {
		printf("===encrypt\n");
		enc->dump();
		printf("\n");
	}

	return(enc);
}

OCTBUF *
MfDESCBC_2::decryptOctets(const OCTBUF &cipher, const PObjectList &a,
	OCTBUF *&ivec) const
{
	if(DBGFLAGS('D')) {
		printf("===cipher\n");
		cipher.dump();
		printf("\n");
	}

        uint32_t lcipher	= cipher.length();
        uint32_t ivlen		= ivecLength();
        uint32_t ldec		= lcipher - ivlen;

	OCTSTR cp = (OCTSTR)cipher.string();

	if(ivlen > 0) {
		if(!ivec) {
			ivec = new PvOctets(0);
		}

		ivec->set(ivlen, cp);
	}

	PvOctets ivwork(ivlen, cp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDESCBC_2", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *dec = new PvOctets(ldec);
	OCTSTR os = dec->buffer();
	OCTSTR is = cp + ivlen;

	PObject *p0 = a[0];
	PvOctets ipsec_key;
	p0->generateOctetsWith(ipsec_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDESCBC_2", "key",
	ipsec_key.buffer(), ipsec_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &ipsec_key;

	OCTSTR wp = ivwork.buffer();

	if(!alignmentCheck(ldec, a)) {
		MfCrypt::decrypt(os,is,ldec,k,wp);
	} else {
		decrypt(os,is,ldec,k,wp);
	}

	if(DBGFLAGS('D')) {
		printf("===decrypt\n");
		dec->dump();
		printf("\n");
	}

	return(dec);
}

OCTBUF *
MfDES3CBC_2::encryptOctets(const OCTBUF &text,
	const PObjectList &a, OCTBUF *ivec) const
{
	if(DBGFLAGS('E')) {
		printf("===text\n");
		text.dump();
		printf("\n");
	}

        uint32_t lenc = text.length();

	OCTSTR ivp = 0;
	PvOctets ipsec_ivec;

        if(a.size() > 1) {
		PObject *p1 = a[1];

		if(p1->generateOctetsWith(ipsec_ivec, 0)) {
			ivp = ipsec_ivec.buffer();
		}
	}

	uint32_t ivlen = ivecLength();
	if(ivec) {
		ivec->set(ivlen, ivp);
	}

	PvOctets ivwork(ivlen, ivp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDES3CBC_2", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *enc = new PvOctets(ivlen + lenc);
	OCTSTR os = enc->buffer();
	OCTSTR is = (OCTSTR)text.string();

	PObject *p0 = a[0];
	PvOctets ipsec_key;
	p0->generateOctetsWith(ipsec_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDES3CBC_2", "key",
	ipsec_key.buffer(), ipsec_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &ipsec_key;

	OCTSTR wp = ivwork.buffer();
	memcpy(os, wp, ivlen);
	os += ivlen;

	if(!alignmentCheck(lenc, a)) {
		MfCrypt::encrypt(os, is, lenc, k, wp);
	} else {
		encrypt(os, is, lenc, k, wp);
	}

	if(DBGFLAGS('E')) {
		printf("===encrypt\n");
		enc->dump();
		printf("\n");
	}

	return(enc);
}

OCTBUF *
MfDES3CBC_2::decryptOctets(const OCTBUF &cipher, const PObjectList &a,
	OCTBUF *&ivec) const
{
	if(DBGFLAGS('D')) {
		printf("===cipher\n");
		cipher.dump();
		printf("\n");
	}

        uint32_t lcipher	= cipher.length();
        uint32_t ivlen		= ivecLength();
        uint32_t ldec		= lcipher - ivlen;

	OCTSTR cp = (OCTSTR)cipher.string();

	if(ivlen > 0) {
		if(!ivec) {
			ivec = new PvOctets(0);
		}

		ivec->set(ivlen, cp);
	}

	PvOctets ivwork(ivlen, cp, true);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDES3CBC_2", "ivec",
	ivwork.buffer(), ivwork.length());
#endif	// ISAKMP_DBG
	PvOctets *dec = new PvOctets(ldec);
	OCTSTR os = dec->buffer();
	OCTSTR is = cp + ivlen;

	PObject *p0 = a[0];
	PvOctets ipsec_key;
	p0->generateOctetsWith(ipsec_key, 0);
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfDES3CBC_2", "key",
	ipsec_key.buffer(), ipsec_key.length());
#endif	// ISAKMP_DBG
	const PObject *k = &ipsec_key;

	OCTSTR wp = ivwork.buffer();
	if(!alignmentCheck(ldec, a)) {
		MfCrypt::decrypt(os,is,ldec,k,wp);
	} else {
		decrypt(os,is,ldec,k,wp);
	}

	if(DBGFLAGS('D')) {
		printf("===decrypt\n");
		dec->dump();
		printf("\n");
	}

	return(dec);
}

MfRIJNDAEL::MfRIJNDAEL(CSTR s,uint8_t k,uint8_t i,uint8_t a):MfCryptKey(s,k,i,a) {}
MfRIJNDAEL::~MfRIJNDAEL() {}
MfAuth::MfAuth(CSTR s,uint8_t i,uint8_t a):MvFunction(s),
	icvLength_(i),alignUnit_(a),dummy_(0) {}
MfAuth::~MfAuth() {}
MfHMAC::MfHMAC(CSTR s,uint8_t i,uint8_t a):MfAuth(s,i,a) {}
MfHMAC::~MfHMAC() {}
MfHMACMD5::MfHMACMD5(CSTR s,uint8_t i,uint8_t a):MfHMAC(s,i,a) {}
MfHMACMD5::~MfHMACMD5() {}

MfHMACMD5_2::MfHMACMD5_2(CSTR s, uint8_t i, uint8_t a): MfHMACMD5(s, i, a) {}
MfHMACMD5_2::~MfHMACMD5_2() {}

bool
MfHMACMD5_2::checkArgument(const PFunction &o,const PObjectList &a) const
{
	bool rc = true;
	uint32_t n = a.size();
	CSTR name = o.metaString();

	if(n != 0 && n != 1) {
		o.error("E %s must have 0 or 1 argument, not %d",name,n);
		return(false);
	}

	if(n == 0) {
		return(true);
	}

	if(!a[0]->isOctets()) {
		o.error("E %s first argument has to be octet stream", name);
		rc = false;
	}

	return(rc);
}

OCTSTR
MfHMACMD5_2::init(OCTSTR cp, const PObjectList &a) const
{
	HMAC_CTX *ctx = cp != 0? (HMAC_CTX*)cp: new HMAC_CTX;
	COCTSTR key = 0;
	uint32_t keylen = 0;
	PvOctets hash_key;

	if(a.size() > 0) {
		PObject *p0 = a[0];

		if(p0->generateOctetsWith(hash_key, 0)) {
			key	= hash_key.buffer();
			keylen	= hash_key.length();
		}
	}

	HMAC_Init(ctx, (OCTSTR)key, keylen, evp());
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfHMACMD5_2", "HMAC_Init(key)", key, keylen);
#endif	// ISAKMP_DBG
	return((OCTSTR)ctx);
}

MfHMACSHA1::MfHMACSHA1(CSTR s,uint8_t i,uint8_t a):MfHMAC(s,i,a) {}
MfHMACSHA1::~MfHMACSHA1() {}

MfHMACSHA1_2::MfHMACSHA1_2(CSTR s, uint8_t i, uint8_t a): MfHMACSHA1(s, i, a) {}
MfHMACSHA1_2::~MfHMACSHA1_2() {}

bool
MfHMACSHA1_2::checkArgument(const PFunction &o,const PObjectList &a) const
{
	bool rc = true;
	uint32_t n = a.size();
	CSTR name = o.metaString();

	if(n != 0 && n != 1) {
		o.error("E %s must have 0 or 1 argument, not %d",name,n);
		return(false);
	}

	if(n == 0) {
		return(true);
	}

	if(!a[0]->isOctets()) {
		o.error("E %s first argument has to be octet stream", name);
		rc = false;
	}

	return(rc);
}

OCTSTR
MfHMACSHA1_2::init(OCTSTR cp, const PObjectList &a) const
{
	HMAC_CTX *ctx = cp != 0? (HMAC_CTX*)cp: new HMAC_CTX;

	COCTSTR key = 0;
	uint32_t keylen = 0;
	PvOctets hash_key;

	if(a.size() > 0) {
		PObject *p0 = a[0];

		if(p0->generateOctetsWith(hash_key, 0)) {
			key	= hash_key.buffer();
			keylen	= hash_key.length();
		}
	}

	HMAC_Init(ctx, (OCTSTR)key, keylen, evp());
#ifdef ISAKMP_DBG
dmp("/tmp/isakmp_dbg.txt", "MfHMACSHA1_2", "HMAC_Init(key)", key, keylen);
#endif	// ISAKMP_DBG
	return((OCTSTR)ctx);
}

MfHMACSHA2_256::MfHMACSHA2_256(CSTR s,uint8_t i,uint8_t a):MfHMAC(s,i,a) {}
MfHMACSHA2_256::~MfHMACSHA2_256() {}
MfHMACSHA2_384::MfHMACSHA2_384(CSTR s,uint8_t i,uint8_t a):MfHMAC(s,i,a) {}
MfHMACSHA2_384::~MfHMACSHA2_384() {}
MfHMACSHA2_512::MfHMACSHA2_512(CSTR s,uint8_t i,uint8_t a):MfHMAC(s,i,a) {}
MfHMACSHA2_512::~MfHMACSHA2_512() {}

MfESPPad* MfESPPad::defaultPadding_=0;
MfESPPad* MfESPPad::IKEdefaultPadding_=0;


syntax highlighted by Code2HTML, v. 0.9.1