#!/bin/sh

if ! [ -d $PWD/src ] && [ -z "$2" ] ; then
	echo "usage: ./build/codegen_ecdh_factory.sh CURVE_NAME"
	return 1
fi

CN="$1"
case $CN in
	"SECP256K1") BN="256_28" ;;
	*) echo "BIG name not defined for the curve ${CN}"; exit -1;  
esac

FILE="${2:-src/zen_ecdh_factory.c}"

cat <<EOF > "${FILE}"
// Generated by build/codegen_ecdh_factory.sh
// `date`
#include <string.h>
#include <lauxlib.h>
#include <zen_ecdh.h>
#include <zen_error.h>
#include <ecdh_${CN}.h>
#include <ecp_${CN}.h>

static char ORDER[MODBYTES_${BN}];
static char PRIME[MODBYTES_${BN}];

#define Cof CURVE_Cof_I_${CN};

void ecdh_init(lua_State *L, ecdh *ECDH) {
	ECDH->fieldsize = EFS_${CN};
	ECDH->hash = HASH_TYPE_${CN};
	ECDH->ECP__KEY_PAIR_GENERATE = ECP_${CN}_KEY_PAIR_GENERATE;
	ECDH->ECP__PUBLIC_KEY_VALIDATE	= ECP_${CN}_PUBLIC_KEY_VALIDATE;
	ECDH->ECP__SVDP_DH = ECP_${CN}_SVDP_DH;
	ECDH->ECP__ECIES_ENCRYPT = ECP_${CN}_ECIES_ENCRYPT;
	ECDH->ECP__ECIES_DECRYPT = ECP_${CN}_ECIES_DECRYPT;
	ECDH->ECP__SP_DSA = ECP_${CN}_SP_DSA;
	ECDH->ECP__SP_DSA_DET = ECP_${CN}_SP_DSA_DET;
	ECDH->ECP__SP_DSA_DET_NOHASH = ECP_${CN}_SP_DSA_DET_NOHASH;
	ECDH->ECP__VP_DSA = ECP_${CN}_VP_DSA;
	ECDH->ECP__SP_DSA_NOHASH = ECP_${CN}_SP_DSA_NOHASH;
	ECDH->ECP__VP_DSA_NOHASH = ECP_${CN}_VP_DSA_NOHASH;
	ECDH->ECP__PUBLIC_KEY_RECOVERY = ECP_${CN}_PUBLIC_KEY_RECOVERY;
	BIG_${BN} tmp; // toBytes takes a non const BIG
	BIG_${BN}_rcopy(tmp, CURVE_Order_${CN});
	BIG_${BN}_toBytes(ORDER, tmp);
	ECDH->order = ORDER;
	ECDH->cofactor = Cof;
	BIG_${BN}_rcopy(tmp, Modulus_${CN});
	BIG_${BN}_toBytes(PRIME, tmp);
	ECDH->prime = PRIME;
	ECDH->mod_size = MODBYTES_${BN};
	act(L,"ECDH curve is ${CN}");
}

/*
   Takes two points on the curve ECDH (in the form of a public key),
   add them and return the point (as a public key not compressed)

   @param pk1 addendum point
   @param pk2 addendum point
   @return sum result
*/
extern ecdh ECDH;

int ecdh_add(lua_State *L) {
	BEGIN();
	char *failed_msg = NULL;
	octet *pk1 = o_arg(L, 1);
	octet *pk2 = o_arg(L, 2);
	if(pk1 == NULL || pk2 == NULL) {
		failed_msg = "Could not allocate public key";
		goto end;
	}
	if((*ECDH.ECP__PUBLIC_KEY_VALIDATE)(pk1)!=0 ||
	   (*ECDH.ECP__PUBLIC_KEY_VALIDATE)(pk2)!=0) {
		failed_msg = "Invalid public key passed as argument";
		goto end;
	}
	ECP_${CN} p1, p2;
	// Export public key to octet.  This is like o_dup but skips
	// first byte since that is used internally by Milagro as a
	// prefix for Montgomery (2) or non-Montgomery curves (4)
	octet *pk_sum = o_new(L, pk1->len);
	if(pk_sum == NULL) {
		failed_msg = "Could not create public key";
		goto end;
	}
	ECP_${CN}_fromOctet(&p1, pk1);
	ECP_${CN}_fromOctet(&p2, pk2);
	ECP_${CN}_add(&p1, &p2);
	ECP_${CN}_toOctet(pk_sum, &p1, false);
end:
	o_free(L, pk1);
	o_free(L, pk2);
	if(failed_msg) {
		THROW(failed_msg);
	}
	return 1;
}


EOF
