/*
 * Copyright 2015-2016 Rivoreo
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 2 of the License, or (at your
 * option) any later version.
 *
 * 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. See the GNU General Public License for
 * more details.
 */

// This code is translated from the C code which is in the public domain.

package org.rivoreo.crypto;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.SecureRandom;

public abstract class ECIES {
	static final int DEGREE_sect163r2 = 163;
/*
	static final byte[] POLY_sect163r2 = { (byte)0xc9, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
	static final byte[] COEFF_B_sect163r2 = { (byte)0xfd, (byte)0x05, (byte)0x32, (byte)0x4a, (byte)0x74, (byte)0x78, (byte)0x2f, (byte)0x51, (byte)0x10, (byte)0xeb, (byte)0x81, (byte)0x14, (byte)0xca, (byte)0x53, (byte)0xc9, (byte)0xb8, (byte)0x07, (byte)0x19, (byte)0x60, (byte)0x0a, };
	static final byte[] BASE_X_sect163r2 = { (byte)0x36, (byte)0x3e, (byte)0x34, (byte)0xe8, (byte)0x37, (byte)0x46, (byte)0x99, (byte)0xd4, (byte)0x68, (byte)0x11, (byte)0x99, (byte)0xa0, (byte)0x7e, (byte)0xd5, (byte)0xa2, (byte)0x86, (byte)0x62, (byte)0xa1, (byte)0xeb, (byte)0xf0, };
	static final byte[] BASE_Y_sect163r2 = { (byte)0xf1, (byte)0x24, (byte)0x73, (byte)0x79, (byte)0x0c, (byte)0x5c, (byte)0x1c, (byte)0xb1, (byte)0x45, (byte)0xd5, (byte)0xcd, (byte)0xa2, (byte)0x4f, (byte)0x09, (byte)0xa0, (byte)0x71, (byte)0x6c, (byte)0xbc, (byte)0x1f, (byte)0xd5, };
	static final byte[] BASE_ORDER_sect163r2 = { (byte)0x33, (byte)0x4c, (byte)0x23, (byte)0xa4, (byte)0x12, (byte)0x0c, (byte)0xe7, (byte)0x77, (byte)0xfe, (byte)0x92, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
*/
	static final int DEGREE_sect233r1 = 233;
/*
	static final byte[] POLY_sect233r1 = { (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
	static final byte[] COEFF_B_sect233r1 = { (byte)0xad, (byte)0x90, (byte)0x8f, (byte)0x7d, (byte)0x5f, (byte)0x11, (byte)0xfe, (byte)0x81, (byte)0x42, (byte)0xce, (byte)0xe9, (byte)0x20, (byte)0x3b, (byte)0x33, (byte)0x3b, (byte)0x21, (byte)0x58, (byte)0xbb, (byte)0x23, (byte)0x09, (byte)0x8c, (byte)0x7f, (byte)0x2c, (byte)0x33, (byte)0x6c, (byte)0xde, (byte)0x7e, (byte)0x64, (byte)0x66, };
	static final byte[] BASE_X_sect233r1 = { (byte)0x8b, (byte)0x55, (byte)0xfd, (byte)0x71, (byte)0x73, (byte)0xeb, (byte)0xf8, (byte)0xf8, (byte)0x36, (byte)0x8b, (byte)0x1f, (byte)0x39, (byte)0xbc, (byte)0x65, (byte)0xef, (byte)0x5f, (byte)0x75, (byte)0xbb, (byte)0xf1, (byte)0x39, (byte)0x21, (byte)0xbb, (byte)0x13, (byte)0x83, (byte)0xac, (byte)0xcb, (byte)0xdf, (byte)0xc9, (byte)0xfa, };
	static final byte[] BASE_Y_sect233r1 = { (byte)0x52, (byte)0x10, (byte)0xf8, (byte)0x01, (byte)0x7e, (byte)0x6f, (byte)0x71, (byte)0x36, (byte)0xca, (byte)0xa7, (byte)0x67, (byte)0xf8, (byte)0xef, (byte)0x0b, (byte)0x8a, (byte)0xbf, (byte)0xbe, (byte)0x28, (byte)0x85, (byte)0xe5, (byte)0x78, (byte)0x06, (byte)0x35, (byte)0x03, (byte)0x19, (byte)0xa4, (byte)0x08, (byte)0x6a, (byte)0x00, };
	static final byte[] BASE_ORDER_sect233r1 = { (byte)0xd7, (byte)0xe0, (byte)0xcf, (byte)0x03, (byte)0x26, (byte)0x1d, (byte)0x03, (byte)0x22, (byte)0x69, (byte)0x8a, (byte)0x2f, (byte)0xe7, (byte)0x74, (byte)0xe9, (byte)0x13, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
*/
	static final int DEGREE_sect283r1 = 283;
/*
	static final byte[] POLY_sect283r1 = { (byte)0xA1, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
	static final byte[] COEFF_B_sect283r1 = { (byte)0xf5, (byte)0xa2, (byte)0x79, (byte)0x3b, (byte)0x31, (byte)0x3e, (byte)0x26, (byte)0xf6, (byte)0x5a, (byte)0x48, (byte)0x81, (byte)0xa5, (byte)0xa2, (byte)0x9f, (byte)0x30, (byte)0x45, (byte)0x76, (byte)0xfd, (byte)0x97, (byte)0xca, (byte)0x3f, (byte)0x30, (byte)0xa0, (byte)0x19, (byte)0x8a, (byte)0xaf, (byte)0xa4, (byte)0xa5, (byte)0x6d, (byte)0x59, (byte)0xb8, (byte)0xc8, (byte)0x0a, (byte)0x68, (byte)0x7b, };
	static final byte[] BASE_X_sect283r1 = { (byte)0x53, (byte)0x20, (byte)0xb1, (byte)0x86, (byte)0xcd, (byte)0xbe, (byte)0xcd, (byte)0xf8, (byte)0x98, (byte)0xe1, (byte)0xe2, (byte)0x80, (byte)0x9c, (byte)0xac, (byte)0x7e, (byte)0x55, (byte)0xb8, (byte)0x25, (byte)0xed, (byte)0x2e, (byte)0xec, (byte)0xdf, (byte)0xb0, (byte)0x70, (byte)0x8c, (byte)0x4f, (byte)0x93, (byte)0xe1, (byte)0x90, (byte)0xdd, (byte)0xb7, (byte)0x8d, (byte)0x25, (byte)0x39, (byte)0xf9, };
	static final byte[] BASE_Y_sect283r1 = { (byte)0xf4, (byte)0x12, (byte)0x81, (byte)0xbe, (byte)0x45, (byte)0xdf, (byte)0xf0, (byte)0x13, (byte)0xc8, (byte)0x79, (byte)0x67, (byte)0x82, (byte)0xb0, (byte)0xdd, (byte)0x0e, (byte)0x35, (byte)0x02, (byte)0xf7, (byte)0x6f, (byte)0x51, (byte)0xb4, (byte)0x02, (byte)0x0d, (byte)0xb2, (byte)0xd4, (byte)0xe6, (byte)0x8f, (byte)0xb9, (byte)0x1c, (byte)0x14, (byte)0x24, (byte)0xfe, (byte)0x54, (byte)0x68, (byte)0x67, };
	static final byte[] BASE_ORDER_sect283r1 = { (byte)0x07, (byte)0xb3, (byte)0xad, (byte)0xef, (byte)0x7c, (byte)0x2a, (byte)0x04, (byte)0x5b, (byte)0x16, (byte)0x90, (byte)0x8a, (byte)0x93, (byte)0xfc, (byte)0x60, (byte)0x96, (byte)0x39, (byte)0x90, (byte)0xef, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, };
*/
	static final int DEGREE_sect409r1 = 409;
/*
	static final byte[] POLY_sect409r1 = { (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
	static final byte[] COEFF_B_sect409r1 = { (byte)0x5f, (byte)0x54, (byte)0x13, (byte)0x7b, (byte)0x31, (byte)0xae, (byte)0x50, (byte)0x4f, (byte)0xaa, (byte)0x55, (byte)0x7a, (byte)0xd5, (byte)0x6c, (byte)0x2f, (byte)0x82, (byte)0x72, (byte)0xb2, (byte)0x97, (byte)0xa1, (byte)0xa9, (byte)0xc8, (byte)0x27, (byte)0xac, (byte)0xd6, (byte)0x99, (byte)0xfa, (byte)0x61, (byte)0x47, (byte)0x67, (byte)0xdd, (byte)0xf3, (byte)0xf1, (byte)0x2e, (byte)0x42, (byte)0xd6, (byte)0x7f, (byte)0x6b, (byte)0x47, (byte)0x7b, (byte)0x3b, (byte)0x75, (byte)0x9a, (byte)0x4b, (byte)0x5c, (byte)0xeb, (byte)0x9f, (byte)0xee, (byte)0xc8, (byte)0xc2, (byte)0xa5, (byte)0x21, };
	static final byte[] BASE_X_sect409r1 = { (byte)0xa7, (byte)0x96, (byte)0x79, (byte)0xbb, (byte)0x54, (byte)0x4e, (byte)0x79, (byte)0x60, (byte)0xab, (byte)0xae, (byte)0x03, (byte)0x56, (byte)0x51, (byte)0x80, (byte)0x11, (byte)0x8a, (byte)0x86, (byte)0x5a, (byte)0x25, (byte)0xdc, (byte)0x03, (byte)0x97, (byte)0xe5, (byte)0x34, (byte)0x5b, (byte)0xfe, (byte)0x1f, (byte)0xb0, (byte)0x4d, (byte)0x1d, (byte)0x77, (byte)0xf1, (byte)0x4a, (byte)0xde, (byte)0x1c, (byte)0x44, (byte)0x60, (byte)0x62, (byte)0x75, (byte)0x64, (byte)0x60, (byte)0x0c, (byte)0x6b, (byte)0x49, (byte)0xb3, (byte)0xdd, (byte)0x88, (byte)0xd0, (byte)0x60, (byte)0x48, (byte)0x5d, };
	static final byte[] BASE_Y_sect409r1 = { (byte)0x06, (byte)0xc7, (byte)0x73, (byte)0x02, (byte)0xba, (byte)0x64, (byte)0xc3, (byte)0x81, (byte)0x36, (byte)0x1b, (byte)0x18, (byte)0xd2, (byte)0x40, (byte)0x4f, (byte)0x4b, (byte)0xdf, (byte)0x1f, (byte)0x4f, (byte)0x51, (byte)0x38, (byte)0x8f, (byte)0xd0, (byte)0x88, (byte)0x54, (byte)0x4f, (byte)0xaa, (byte)0x58, (byte)0x01, (byte)0x8d, (byte)0x19, (byte)0xbd, (byte)0xa7, (byte)0xc5, (byte)0xb9, (byte)0x36, (byte)0x76, (byte)0x6a, (byte)0x10, (byte)0xed, (byte)0x24, (byte)0x83, (byte)0xa7, (byte)0xbf, (byte)0x2b, (byte)0xf3, (byte)0xe5, (byte)0x6b, (byte)0xab, (byte)0xcf, (byte)0xb1, (byte)0x61, };
	static final byte[] BASE_ORDER_sect409r1 = { (byte)0x73, (byte)0x11, (byte)0xa2, (byte)0xd9, (byte)0x37, (byte)0xcd, (byte)0x64, (byte)0x81, (byte)0x83, (byte)0x2f, (byte)0x05, (byte)0x9e, (byte)0x3c, (byte)0x7c, (byte)0xa4, (byte)0x5f, (byte)0xbe, (byte)0x07, (byte)0x33, (byte)0xf3, (byte)0x12, (byte)0xa6, (byte)0xd6, (byte)0xaa, (byte)0xe2, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
*/
	static final int DEGREE_sect571r1 = 571;
/*
	static final byte[] POLY_sect571r1 = { (byte)0x25, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, };
	static final byte[] COEFF_B_sect571r1 = { (byte)0x7a, (byte)0x72, (byte)0x55, (byte)0x29, (byte)0x7f, (byte)0xff, (byte)0xfe, (byte)0x7f, (byte)0x0c, (byte)0xca, (byte)0xba, (byte)0x39, (byte)0xe7, (byte)0x4d, (byte)0x0e, (byte)0x52, (byte)0xaa, (byte)0x12, (byte)0xff, (byte)0x78, (byte)0x5a, (byte)0x18, (byte)0xfd, (byte)0x4a, (byte)0x29, (byte)0x6e, (byte)0xa6, (byte)0x56, (byte)0x67, (byte)0xad, (byte)0xe7, (byte)0x2b, (byte)0x33, (byte)0x59, (byte)0xfa, (byte)0x8e, (byte)0xbd, (byte)0xab, (byte)0xff, (byte)0x84, (byte)0xad, (byte)0x18, (byte)0x9a, (byte)0x4a, (byte)0xce, (byte)0xa8, (byte)0x6b, (byte)0xcd, (byte)0xf1, (byte)0xef, (byte)0x8c, (byte)0xcb, (byte)0xff, (byte)0x97, (byte)0x6a, (byte)0x5c, (byte)0x2f, (byte)0xd6, (byte)0xf3, (byte)0xb7, (byte)0x17, (byte)0x71, (byte)0x29, (byte)0xde, (byte)0x95, (byte)0xf2, (byte)0x21, (byte)0x22, (byte)0x7e, (byte)0x0e, (byte)0xf4, };
	static final byte[] BASE_X_sect571r1 = { (byte)0x19, (byte)0x2d, (byte)0xec, (byte)0x8e, (byte)0x9c, (byte)0x76, (byte)0xe7, (byte)0xe1, (byte)0x27, (byte)0xd9, (byte)0x50, (byte)0xc8, (byte)0xb4, (byte)0xa3, (byte)0xbf, (byte)0x4a, (byte)0x39, (byte)0xf1, (byte)0x14, (byte)0x86, (byte)0x03, (byte)0x60, (byte)0xae, (byte)0x99, (byte)0x14, (byte)0xfb, (byte)0x67, (byte)0x5b, (byte)0xa3, (byte)0x11, (byte)0xd7, (byte)0xcd, (byte)0x93, (byte)0xd2, (byte)0xc0, (byte)0xf4, (byte)0x50, (byte)0x39, (byte)0xe5, (byte)0xbd, (byte)0xbd, (byte)0x2a, (byte)0x7b, (byte)0xdb, (byte)0xc8, (byte)0x0f, (byte)0xf4, (byte)0xa5, (byte)0x0a, (byte)0xa8, (byte)0x5f, (byte)0x95, (byte)0xd2, (byte)0xd1, (byte)0x93, (byte)0x0a, (byte)0x75, (byte)0xd7, (byte)0x3c, (byte)0x0d, (byte)0xd4, (byte)0xc0, (byte)0x16, (byte)0x6c, (byte)0x29, (byte)0x56, (byte)0xb8, (byte)0x34, (byte)0x1d, (byte)0x00, (byte)0x03, };
	static final byte[] BASE_Y_sect571r1 = { (byte)0x5b, (byte)0xc1, (byte)0x8a, (byte)0x1b, (byte)0xaf, (byte)0x27, (byte)0x48, (byte)0x1a, (byte)0x3c, (byte)0xdd, (byte)0x23, (byte)0x6e, (byte)0x51, (byte)0xf1, (byte)0xe2, (byte)0x16, (byte)0x9b, (byte)0xc1, (byte)0x85, (byte)0x04, (byte)0x2f, (byte)0x1d, (byte)0x53, (byte)0xb3, (byte)0xa8, (byte)0xb2, (byte)0x1b, (byte)0x46, (byte)0x8f, (byte)0xaf, (byte)0x91, (byte)0x62, (byte)0x57, (byte)0x8a, (byte)0xb0, (byte)0xba, (byte)0x43, (byte)0x3e, (byte)0x42, (byte)0x84, (byte)0xa6, (byte)0xe8, (byte)0x21, (byte)0x39, (byte)0x53, (byte)0xf8, (byte)0x80, (byte)0x19, (byte)0xca, (byte)0xbb, (byte)0x9c, (byte)0x00, (byte)0xa6, (byte)0x27, (byte)0x6c, (byte)0x8c, (byte)0xd7, (byte)0x69, (byte)0x3d, (byte)0xb7, (byte)0xfe, (byte)0xff, (byte)0xcc, (byte)0x6d, (byte)0x9b, (byte)0x63, (byte)0xda, (byte)0x42, (byte)0x73, (byte)0xf2, (byte)0x7b, };
	static final byte[] BASE_ORDER_sect571r1 = { (byte)0x47, (byte)0x4e, (byte)0xe8, (byte)0x2f, (byte)0xbb, (byte)0xe9, (byte)0x82, (byte)0x83, (byte)0x6e, (byte)0xd6, (byte)0x74, (byte)0x51, (byte)0x3d, (byte)0xe9, (byte)0x1d, (byte)0x16, (byte)0xa1, (byte)0x9c, (byte)0xdd, (byte)0xc7, (byte)0x1e, (byte)0x85, (byte)0x23, (byte)0x68, (byte)0x18, (byte)0x9b, (byte)0x05, (byte)0x08, (byte)0x73, (byte)0x98, (byte)0x55, (byte)0xff, (byte)0x18, (byte)0xce, (byte)0x61, (byte)0xe6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, };
*/

	static final int[] POLY_sect163r2 = { 0x000000c9, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8 };
	static final int[] COEFF_B_sect163r2 = { 0x4a3205fd, 0x512f7874, 0x1481eb10, 0xb8c953ca, 0x0a601907, 0x2 };
	static final int[] BASE_X_sect163r2 = { 0xe8343e36, 0xd4994637, 0xa0991168, 0x86a2d57e, 0xf0eba162, 0x3 };
	static final int[] BASE_Y_sect163r2 = { 0x797324f1, 0xb11c5c0c, 0xa2cdd545, 0x71a0094f, 0xd51fbc6c, 0x0 };
	static final int[] BASE_ORDER_sect163r2 = { 0xa4234c33, 0x77e70c12, 0x000292fe, 0x00000000, 0x00000000, 0x4 };
	static final int[] POLY_sect233r1 = { 0x00000001, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x200 };
	static final int[] COEFF_B_sect233r1 = { 0x7d8f90ad, 0x81fe115f, 0x20e9ce42, 0x213b333b, 0x0923bb58, 0x332c7f8c, 0x647ede6c, 0x066 };
	static final int[] BASE_X_sect233r1 = { 0x71fd558b, 0xf8f8eb73, 0x391f8b36, 0x5fef65bc, 0x39f1bb75, 0x8313bb21, 0xc9dfcbac, 0x0fa };
	static final int[] BASE_Y_sect233r1 = { 0x01f81052, 0x36716f7e, 0xf867a7ca, 0xbf8a0bef, 0xe58528be, 0x03350678, 0x6a08a419, 0x100 };
	static final int[] BASE_ORDER_sect233r1 = { 0x03cfe0d7, 0x22031d26, 0xe72f8a69, 0x0013e974, 0x00000000, 0x00000000, 0x00000000, 0x100 };
	static final int[] POLY_sect283r1 = { 0x000010A1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8000000 };
	static final int[] COEFF_B_sect283r1 = { 0x3b79a2f5, 0xf6263e31, 0xa581485a, 0x45309fa2, 0xca97fd76, 0x19a0303f, 0xa5a4af8a, 0xc8b8596d, 0x27b680a };
	static final int[] BASE_X_sect283r1 = { 0x86b12053, 0xf8cdbecd, 0x80e2e198, 0x557eac9c, 0x2eed25b8, 0x70b0dfec, 0xe1934f8c, 0x8db7dd90, 0x5f93925 };
	static final int[] BASE_Y_sect283r1 = { 0xbe8112f4, 0x13f0df45, 0x826779c8, 0x350eddb0, 0x516ff702, 0xb20d02b4, 0xb98fe6d4, 0xfe24141c, 0x3676854 };
	static final int[] BASE_ORDER_sect283r1 = { 0xefadb307, 0x5b042a7c, 0x938a9016, 0x399660fc, 0xffffef90, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ffffff };
	static final int[] POLY_sect409r1 = { 0x00000001, 0x00000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2000000 };
	static final int[] COEFF_B_sect409r1 = { 0x7b13545f, 0x4f50ae31, 0xd57a55aa, 0x72822f6c, 0xa9a197b2, 0xd6ac27c8, 0x4761fa99, 0xf1f3dd67, 0x7fd6422e, 0x3b7b476b, 0x5c4b9a75, 0xc8ee9feb, 0x021a5c2 };
	static final int[] BASE_X_sect409r1 = { 0xbb7996a7, 0x60794e54, 0x5603aeab, 0x8a118051, 0xdc255a86, 0x34e59703, 0xb01ffe5b, 0xf1771d4d, 0x441cde4a, 0x64756260, 0x496b0c60, 0xd088ddb3, 0x15d4860 };
	static final int[] BASE_Y_sect409r1 = { 0x0273c706, 0x81c364ba, 0xd2181b36, 0xdf4b4f40, 0x38514f1f, 0x5488d08f, 0x0158aa4f, 0xa7bd198d, 0x7636b9c5, 0x24ed106a, 0x2bbfa783, 0xab6be5f3, 0x061b1cf };
	static final int[] BASE_ORDER_sect409r1 = { 0xd9a21173, 0x8164cd37, 0x9e052f83, 0x5fa47c3c, 0xf33307be, 0xaad6a612, 0x000001e2, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1000000 };
	static final int[] POLY_sect571r1 = { 0x00000425, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8000000 };
	static final int[] COEFF_B_sect571r1 = { 0x2955727a, 0x7ffeff7f, 0x39baca0c, 0x520e4de7, 0x78ff12aa, 0x4afd185a, 0x56a66e29, 0x2be7ad67, 0x8efa5933, 0x84ffabbd, 0x4a9a18ad, 0xcd6ba8ce, 0xcb8ceff1, 0x5c6a97ff, 0xb7f3d62f, 0xde297117, 0x2221f295, 0x2f40e7e };
	static final int[] BASE_X_sect571r1 = { 0x8eec2d19, 0xe1e7769c, 0xc850d927, 0x4abfa3b4, 0x8614f139, 0x99ae6003, 0x5b67fb14, 0xcdd711a3, 0xf4c0d293, 0xbde53950, 0xdb7b2abd, 0xa5f40fc8, 0x955fa80a, 0x0a93d1d2, 0x0d3cd775, 0x6c16c0d4, 0x34b85629, 0x303001d };
	static final int[] BASE_Y_sect571r1 = { 0x1b8ac15b, 0x1a4827af, 0x6e23dd3c, 0x16e2f151, 0x0485c19b, 0xb3531d2f, 0x461bb2a8, 0x6291af8f, 0xbab08a57, 0x84423e43, 0x3921e8a6, 0x1980f853, 0x009cbbca, 0x8c6c27a6, 0xb73d69d7, 0x6dccfffe, 0x42da639b, 0x37bf273 };
	static final int[] BASE_ORDER_sect571r1 = { 0x2fe84e47, 0x8382e9bb, 0x5174d66e, 0x161de93d, 0xc7dd9ca1, 0x6823851e, 0x08059b18, 0xff559873, 0xe661ce18, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ffffff };


	public enum SECTDomainParameters {
		sect163r2,
		sect233r1,
		sect283r1,
		sect409r1,
		sect571r1
	}

	public ECIES(SECTDomainParameters dp) {
		switch(dp) {
			case sect163r2:
				DEGREE = 163;
				poly = POLY_sect163r2;
				coeff_b = COEFF_B_sect163r2;
				base_x = BASE_X_sect163r2;
				base_y = BASE_Y_sect163r2;
				base_order = BASE_ORDER_sect163r2;
				break;
			case sect233r1:
				DEGREE = 233;
				poly = POLY_sect233r1;
				coeff_b = COEFF_B_sect233r1;
				base_x = BASE_X_sect233r1;
				base_y = BASE_Y_sect233r1;
				base_order = BASE_ORDER_sect233r1;
				break;
			case sect283r1:
				DEGREE = 283;
				poly = POLY_sect283r1;
				coeff_b = COEFF_B_sect283r1;
				base_x = BASE_X_sect283r1;
				base_y = BASE_Y_sect283r1;
				base_order = BASE_ORDER_sect283r1;
				break;
			case sect409r1:
				DEGREE = 409;
				poly = POLY_sect409r1;
				coeff_b = COEFF_B_sect409r1;
				base_x = BASE_X_sect409r1;
				base_y = BASE_Y_sect409r1;
				base_order = BASE_ORDER_sect409r1;
				break;
			case sect571r1:
				DEGREE = 571;
				poly = POLY_sect571r1;
				coeff_b = COEFF_B_sect571r1;
				base_x = BASE_X_sect571r1;
				base_y = BASE_Y_sect571r1;
				base_order = BASE_ORDER_sect571r1;
				break;
			default:
				throw new IllegalArgumentException();
		}
		NUMWORDS = (DEGREE + MARGIN + 31) / 32;
		OVERHEAD = 8 * NUMWORDS + 8;
		KEY_SIZE = (DEGREE + 7) / 8;
		ECIES_START_OVERHEAD = 8 * NUMWORDS;
		ECIES_CHUNK_OVERHEAD = 8;
	}

	final int DEGREE;

	final int[] poly;
	final int[] coeff_b;
	final int[] base_x;
	final int[] base_y;
	final int[] base_order;

	final int MARGIN = 3;
	final int NUMWORDS;
	public final int OVERHEAD;
	public final int KEY_SIZE;

	public class PublicKey {
		public PublicKey() {
			x = new byte[KEY_SIZE];
			y = new byte[KEY_SIZE];
		}
		public final byte[] x, y;
	}

	public class PrivateKey {
		public PrivateKey() {
			k = new byte[KEY_SIZE];
		}
		public PrivateKey(byte[] a) {
			if(a.length != KEY_SIZE) throw new IllegalArgumentException("a.length should be " + KEY_SIZE);
			k = a;
		}
		public final byte[] k;
	}

	// typedef int[NUMWORDS] elem_t
	// typedef int[NUMWORDS] exp_t

	private static int compare_bytes(final byte[] a, final byte[] b) {
		int i = 0;
		while(i < a.length && i < b.length) {
			if(a[i] < b[i]) return -1;
			else if(a[i] > b[i]) return 1;
			i++;
		}
		if(a.length < b.length) return -1;
		else if(a.length > b.length) return 1;
		return 0;
	}

	private static int compare_integers(final int[] a, final int[] b) {
		int i = 0;
		while(i < a.length && i < b.length) {
			if(a[i] < b[i]) return -1;
			else if(a[i] > b[i]) return 1;
			i++;
		}
		if(a.length < b.length) return -1;
		else if(a.length > b.length) return 1;
		return 0;
	}

	private static int compare_bytes(final byte[] a, final byte[] b, int maxlen) {
		if(maxlen < 0) throw new IllegalArgumentException("maxlen < 0");
		if(maxlen == 0) return 0;
		int i = 0;
		while(i < a.length && i < b.length) {
			if(a[i] < b[i]) return -1;
			else if(a[i] > b[i]) return 1;
			if(++i == maxlen) return 0;
		}
		if(a.length < b.length) return -1;
		else if(a.length > b.length) return 1;
		return 0;
	}

	protected static byte[] sub_byte_array(final byte[] a, int start) {
		if(start < 0) throw new IllegalArgumentException("start < 0");
		if(start == 0) return a.clone();
		if(start > a.length) return new byte[0];
		byte[] r = new byte[a.length - start];
		for(int i=0; i<r.length; i++) r[i] = a[start + i];
		return r;
	}

	private static int unsigned_byte_to_int(byte v) {
		return v < 0 ? (int)v + 256 : v;
	}

	protected static long unsigned_int_to_long(int v) {
		return v < 0 ? (long)v + 4294967296l : v;
	}

	protected int unsigned_bytes_to_int(byte[] a, int offset) {
		if(a.length + offset < 4) throw new IllegalArgumentException("a.length + offset < 4");
		return unsigned_byte_to_int(a[offset]) << 24 |
			unsigned_byte_to_int(a[offset + 1]) << 16 |
			unsigned_byte_to_int(a[offset + 2]) << 8 |
			unsigned_byte_to_int(a[offset + 3]);
	}

	protected void int_to_bytes(byte[] dest, int offset, int n) {
		if(dest.length + offset < 4) throw new IllegalArgumentException("dest.length + offset < 4");
		dest[offset] = (byte)((n >>> 24) & 0xff);
		dest[offset + 1] = (byte)((n >>> 16) & 0xff);
		dest[offset + 2] = (byte)((n >>> 8) & 0xff);
		dest[offset + 3] = (byte)((n) & 0xff);
	}

	public PublicKey create_publickey() {
		return new PublicKey();
	}

	public PrivateKey create_privatekey() {
		return new PrivateKey();
	}

	private void bitstr_import(int[] x, final byte[] s) {
		if(x.length != NUMWORDS) throw new IllegalArgumentException("x.length should be " + NUMWORDS);
		//for(x += NUMWORDS, int i = 0; i < NUMWORDS; i++, s += 4) *--x = CHARS2INT(s);
		for(int i = 0, xi = NUMWORDS, si = 0; i < NUMWORDS; i++, si += 4) {
			//x[--xi] = (int)s[si] |
			//	(int)s[si + 1] << 8 |
			//	(int)s[si + 2] << 16 |
			//	(int)s[si + 3] << 24;
			//x[--xi] = to_unsigned_int(s[si]) |
			//	to_unsigned_int(s[si + 1]) << 8 |
			//	to_unsigned_int(s[si + 2]) << 16 |
			//	to_unsigned_int(s[si + 3]) << 24;
			//x[--xi] = to_unsigned_int(s[si]) << 24 |
			//	to_unsigned_int(s[si + 1]) << 16 |
			//	to_unsigned_int(s[si + 2]) << 8 |
			//	to_unsigned_int(s[si + 3]);
			x[--xi] = unsigned_bytes_to_int(s, si);
		}
	}

	private int[] bitstr_import(final byte[] s) {
		int[] r = new int[NUMWORDS];
		bitstr_import(r, s);
		return r;
	}

	private void bitstr_export(byte[] s, int offset, final int[] x) {
		if(offset < 0) throw new IllegalArgumentException("offset < 0");
		if(x.length != NUMWORDS) throw new IllegalArgumentException("x.length should be " + NUMWORDS);
		//for(x += ECIES_NUMWORDS, i = 0; i < ECIES_NUMWORDS; i++, s += 4)
		for(int i = 0, xi = NUMWORDS, si = offset; i < NUMWORDS; i++, si += 4) {
			int v = x[--xi];
			//s[si] = (byte)(v & 0xff);
			//s[si + 1] = (byte)((v >> 8) & 0xff);
			//s[si + 2] = (byte)((v >> 16) & 0xff);
			//s[si + 3] = (byte)((v >> 24) & 0xff);
			s[si] = (byte)((v >>> 24) & 0xff);
			s[si + 1] = (byte)((v >>> 16) & 0xff);
			s[si + 2] = (byte)((v >>> 8) & 0xff);
			s[si + 3] = (byte)((v) & 0xff);
			//int_to_bytes(s, si, v);
		}
	}

	private int bitstr_sizeinbits(final int[] x) {
		int i, xi;
		//for(x += ECIES_NUMWORDS, i = 32 * ECIES_NUMWORDS; i > 0 && ! *--x; i -= 32);
		for(i = 32 * NUMWORDS, xi = NUMWORDS; i > 0 && x[--xi] == 0; i -= 32);
		if(i != 0) for(int mask = 1 << 31; (x[xi] & mask) == 0; mask >>>= 1, i--);
		return i;
	}

	/* left-shift by 'count' digits */
	private void bitstr_lshift(int[] a, final int[] b, int count) {
	/*
		int i, offs = 4 * (count / 32);
		memmove((char*)A + offs, B, sizeof(bitstr_t) - offs);
		memset(A, 0, offs);
		if (count %= 32) {
			for(i = ECIES_NUMWORDS - 1; i > 0; i--)
				A[i] = (A[i] << count) | (A[i - 1] >> (32 - count));
			A[0] <<= count;
		}
*/
		if(a.length != NUMWORDS) throw new IllegalArgumentException("a.length should be " + NUMWORDS);
		if(b.length != NUMWORDS) throw new IllegalArgumentException("b.length should be " + NUMWORDS);
		if(count > NUMWORDS * 32) throw new IllegalArgumentException("count too large");
		int i;
		int shift_items = count / 32;	// count / sizeof(int)
		for(i=0; i<NUMWORDS-shift_items; i++) a[shift_items + i] = b[i];
		for(i=0; i<shift_items; i++) a[i] = 0;
		if((count %= 32) != 0) {
			for(i = NUMWORDS - 1; i > 0; i--) {
				a[i] = (a[i] << count) | (a[i - 1] >>> (32 - count));
			}
			a[0] <<= count;
		}
	}

	private void bitstr_load(int[] bstr, final byte[] data, int len) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::bitstr_load(int[%d], final byte[%d], %d)\n", bstr.length, data.length, len);
		//uint32_t *bptr = bstr + ((len + 3) / 4) - 1;
		int i = (len + 3) / 4 - 1;
		int di = 0;

		len %= 4;

		if(len > 0) {
			//*bptr = 0;
			bstr[i] = 0;
			if(len > 1) {
				if(len > 2) {
					//*bptr |= (uint32_t)(*data++) << 16;
					//bstr[i] |= (int)data[di++] << 16;
					bstr[i] |= unsigned_byte_to_int(data[di++]) << 16;
				}
				//*bptr |= (uint32_t)(*data++) <<  8;
				//bstr[i] |= (int)data[di++] << 8;
				bstr[i] |= unsigned_byte_to_int(data[di++]) << 8;
			}
			//*bptr |= (uint32_t)(*data++);
			//bptr--;
			//bstr[i--] |= (int)data[di++];
			bstr[i--] |= unsigned_byte_to_int(data[di++]);
		}
/*
		for(; bptr >= bstr; bptr--){
			*bptr  = (uint32_t)(*data++) << 24;
			*bptr |= (uint32_t)(*data++) << 16;
			*bptr |= (uint32_t)(*data++) <<  8;
			*bptr |= (uint32_t)(*data++);
		}*/
		while(i >= 0) {
		/*
			bstr[i] = (int)data[di++] << 24;
			bstr[i] |= (int)data[di++] << 16;
			bstr[i] |= (int)data[di++] << 8;
			bstr[i] |= (int)data[di++];
		*/
		/*
			bstr[i] = to_unsigned_int(data[di++]) << 24;
			bstr[i] |= to_unsigned_int(data[di++]) << 16;
			bstr[i] |= to_unsigned_int(data[di++]) << 8;
			bstr[i] |= to_unsigned_int(data[di++]);
			i--;
		*/
			bstr[i--] = unsigned_bytes_to_int(data, di);
			di += 4;
		}
	}

	private void bitstr_dump(byte[] data, int len, final int[] bstr) {
		//const uint32_t *bptr = bstr + ((len + 3) / 4) - 1;
		int i = (len + 3) / 4 - 1;
		int di = 0;
		
		len %= 4;

		if(len > 0){
			if(len > 1){
				if(len > 2){
					//*data++ = *bptr >> 16;
					data[di++] = (byte)(bstr[i] >>> 16);
				}
				//*data++ = *bptr >>  8;
				data[di++] = (byte)(bstr[i] >>> 8);
			}
			//*data++ = *bptr;
			//bptr--;
			data[di++] = (byte)(bstr[i--]);
		}

/*
		for(; bptr >= bstr; bptr--){
			*data++ = *bptr >> 24;
			*data++ = *bptr >> 16;
			*data++ = *bptr >>  8;
			*data++ = *bptr;
		}*/
		while(i >= 0) {
			data[di++] = (byte)(bstr[i] >>> 24);
			data[di++] = (byte)(bstr[i] >>> 16);
			data[di++] = (byte)(bstr[i] >>> 8);
			data[di++] = (byte)(bstr[i--]);
		}
	}

	private boolean bitstr_is_clear(final int[] x) {
		int i;
		for(i = 0; i < NUMWORDS && x[i] == 0; i++);
		return i == NUMWORDS;
	}

	private byte bitstr_getbit(int[] x, int i) {
		return (byte)((x[i / 32] >>> (i % 32)) & 1);
	}

	private void bitstr_setbit(int[] x, int i) {
		x[i / 32] |= 1 << (i % 32);
	}

	private void bitstr_clrbit(int[] x, int i) {
		x[i / 32] &= ~(1 << (i % 32));
	}

	private void bitstr_clear(int[] x) {
		if(x.length != NUMWORDS) throw new IllegalArgumentException("x.length should be " + NUMWORDS);
		for(int i=0; i<NUMWORDS; i++) x[i] = 0;
	}

	private void bitstr_copy(int[] dest, final int[] src) {
		if(dest.length != NUMWORDS) throw new IllegalArgumentException("dest.length should be " + NUMWORDS);
		if(src.length != NUMWORDS) throw new IllegalArgumentException("src.length should be " + NUMWORDS);
		for(int i=0; i<NUMWORDS; i++) dest[i] = src[i];
	}

	private void bitstr_swap(int[] a, int[] b) {
		if(a.length != NUMWORDS) throw new IllegalArgumentException("a.length should be " + NUMWORDS);
		if(b.length != NUMWORDS) throw new IllegalArgumentException("b.length should be " + NUMWORDS);
		int[] t = a.clone();
		bitstr_copy(a, b);
		bitstr_copy(b, t);
	}

	private void field_set1(int[] x) {
		x[0] = 1;
		for(int i = 1; i < NUMWORDS; i++) x[i] = 0;
	}

	private boolean field_is1(final int[] x) {
		int i;
		if(x[0] != 1) return false;
		for(i = 1; i < NUMWORDS && x[i] == 0; i++);
		return i == NUMWORDS;
	}

	private void field_add1(int[] a) {
		//a[0] ^= 1;
		a[0] = (int)(unsigned_int_to_long(a[0]) ^ 1);
	}

	/* field addition */
	private void field_add(int[] z, final int[] x, final int[] y) {
		//for(int i = 0; i < NUMWORDS; i++) z[i] = x[i] ^ y[i];
		for(int i = 0; i < NUMWORDS; i++) {
			z[i] = (int)(unsigned_int_to_long(x[i]) ^ unsigned_int_to_long(y[i]));
		}
	}

	/* field multiplication */
	private void field_mult(int[] z, final int[] x, final int[] y) {
		int[] b = new int[NUMWORDS];
		/* assert(z != y); */
		bitstr_copy(b, x);
		if(bitstr_getbit(y, 0) != 0) bitstr_copy(z, x);
		else bitstr_clear(z);
		for(int i = 1; i < DEGREE; i++) {
			for(int j = NUMWORDS - 1; j > 0; j--) b[j] = (b[j] << 1) | (b[j - 1] >>> 31);
			b[0] <<= 1;
			if(bitstr_getbit(b, DEGREE) != 0) field_add(b, b, poly);
			if(bitstr_getbit(y, i) != 0) field_add(z, z, b);
		}
	}

	private void debug_print_bitstr(int[] a) {
		for(int i=0; i<a.length; i++) System.err.printf("0x%x\n", a[i]);
	}
	protected void debug_print_bytes(byte[] a, int len) {
		for(int i=0; i<len; i++) System.err.printf("0x%x, ", a[i]);
		System.err.println();
	}

	/* field inversion */
	private void field_invert(int[] z, final int[] x) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::field_invert(int[%d], final int[%d])\n", z.length, x.length);
		//debug_print_bitstr(x);
		//elem_t u, v, g, h;
		int[] u = new int[NUMWORDS];
		int[] v = new int[NUMWORDS];
		int[] g = new int[NUMWORDS];
		int[] h = new int[NUMWORDS];
		bitstr_copy(u, x);
		bitstr_copy(v, poly);
		bitstr_clear(g);
		field_set1(z);
		//debug_print_bitstr(z);
		//System.err.printf("%d, %d\n", bitstr_sizeinbits(u), bitstr_sizeinbits(v));
		while(!field_is1(u)) {
			//System.err.printf("u[0] = %d\n", u[0]);
			int i = bitstr_sizeinbits(u) - bitstr_sizeinbits(v);
			//System.err.printf("field_invert: i = %d\n", i);
			if(i < 0) {
				bitstr_swap(u, v);
				bitstr_swap(g, z);
				i = -i;
			}
			bitstr_lshift(h, v, i);
			//debug_print_bitstr(h);
			field_add(u, u, h);
			bitstr_lshift(h, g, i);
			field_add(z, z, h);
		}
	}

	private boolean point_is_zero(final int[] x, final int[] y) {
		return bitstr_is_clear(x) && bitstr_is_clear(y);
	}

	private void point_set_zero(int[] x, int[] y) {
		bitstr_clear(x);
		bitstr_clear(y);
	}

	private void point_copy(int[] dest_x, int[] dest_y, final int[] src_x, final int[] src_y) {
		bitstr_copy(dest_x, src_x);
		bitstr_copy(dest_y, src_y);
	}

	private boolean is_point_on_curve(final int[] x, final int[] y) {
		int[] a = new int[NUMWORDS];
		int[] b = new int[NUMWORDS];
		if(point_is_zero(x, y)) return true;
		field_mult(a, x, x);
		field_mult(b, a, x);
		field_add(a, a, b);
		field_add(a, a, coeff_b);
		field_mult(b, y, y);
		field_add(a, a, b);
		field_mult(b, x, y);
		//return bitstr_is_equal(a, b);
		//return a.equals(b);
		return compare_integers(a, b) == 0;
	}

	private void point_double(int[] x, int[] y) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::point_double(int[%d], int[%d])\n", x.length, y.length);
		//debug_print_bitstr(x);
		if(bitstr_is_clear(x)) {
			bitstr_clear(y);
			return;
		}
		int[] a = new int[NUMWORDS];
		field_invert(a, x);
		field_mult(a, a, y);
		field_add(a, a, x);
		field_mult(y, x, x);
		field_mult(x, a, a);
		field_add1(a);
		field_add(x, x, a);
		field_mult(a, a, x);
		field_add(y, y, a);
	}

	/* add two points together (x1, y1) := (x1, y1) + (x2, y2) */
	private void point_add(int[] x1, int[] y1, final int[] x2, final int[] y2) {
		if(point_is_zero(x2, y2)) return;
		if(point_is_zero(x1, y1)) {
			point_copy(x1, y1, x2, y2);
			return;
		}
		//if(bitstr_is_equal(x1, x2)) {
		if(compare_integers(x1, x2) == 0) {
			//if(bitstr_is_equal(y1, y2)) point_double(x1, y1);
			if(compare_integers(y1, y2) == 0) point_double(x1, y1);
			else point_set_zero(x1, y1);
		} else {
			//elem_t a, b, c, d;
			int[] a = new int[NUMWORDS];
			int[] b = new int[NUMWORDS];
			int[] c = new int[NUMWORDS];
			int[] d = new int[NUMWORDS];
			field_add(a, y1, y2);
			field_add(b, x1, x2);
			field_invert(c, b);
			field_mult(c, c, a);
			field_mult(d, c, c);
			field_add(d, d, c);
			field_add(d, d, b);
			field_add1(d);
			field_add(x1, x1, d);
			field_mult(a, x1, c);
			field_add(a, a, d);
			field_add(y1, y1, a);
			bitstr_copy(x1, d);
		}
	}

	private void point_mult(int[] x, int[] y, final int[] exp) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::point_mult(int[%d], int[%d], final int[%d])\n", x.length, y.length, exp.length);
		//elem_t X, Y;
		int[] X = new int[NUMWORDS];
		int[] Y = new int[NUMWORDS];
		point_set_zero(X, Y);
		for(int i = bitstr_sizeinbits(exp) - 1; i >= 0; i--) {
			//System.err.printf("point_mult: i = %d\n", i);
			point_double(X, Y);
			if(bitstr_getbit(exp, i) != 0) point_add(X, Y, x, y);
		}
		point_copy(x, y, X, Y);
	}

	private int internal_validate_pubkey(final int[] Px, final int[] Py) {
		//System.err.printf("DEGREE = %d\nbitstr_sizeinbits(Px) = %d\nbitstr_sizeinbits(Py) = %d\n",
		//	DEGREE, bitstr_sizeinbits(Px), bitstr_sizeinbits(Py));
		return (bitstr_sizeinbits(Px) > DEGREE) || (bitstr_sizeinbits(Py) > DEGREE) ||
			point_is_zero(Px, Py) || ! is_point_on_curve(Px, Py) ? -1 : 1;
	}

	public void generate_keys(PrivateKey priv, PublicKey pub) {
		//System.err.println("method: org.rivoreo.crypto.ECIES::generate_keys(PrivateKey, PublicKey)");

		int[] x = new int[NUMWORDS];
		int[] y = new int[NUMWORDS];
		int[] k = new int[NUMWORDS];

		get_random_exponent(k);
		point_copy(x, y, base_x, base_y);
		point_mult(x, y, k);

		bitstr_dump(pub.x, KEY_SIZE, x);
		bitstr_dump(pub.y, KEY_SIZE, y);
		bitstr_dump(priv.k, KEY_SIZE, k);
	}

	public int validate_pubkey(final PublicKey pubkey) {
		int[] x = new int[NUMWORDS];
		int[] y = new int[NUMWORDS];
		bitstr_load(x, pubkey.x, KEY_SIZE);
		bitstr_load(y, pubkey.y, KEY_SIZE);
		if(internal_validate_pubkey(x, y) < 0) return -1;
		point_mult(x, y, base_order);
		return point_is_zero(x, y) ? 1 : -1;
	}

	private void get_random_exponent(int[] exp) {
		byte[] buffer = new byte[4 * NUMWORDS];
		//int i = buffer.length - 1;
		FileInputStream fis = null;
		SecureRandom sr = null;
		try {
			fis = new FileInputStream("/dev/urandom");
		} catch(FileNotFoundException e) {
			e.printStackTrace();
			sr = new SecureRandom();
		}
		do {
			try {
				if(fis == null) throw new IOException("/dev/urandom");
				fis.read(buffer);
			} catch(IOException e) {
				if(sr == null) sr = new SecureRandom();
				sr.nextBytes(buffer);
			}
			bitstr_import(exp, buffer);
			for(int i = bitstr_sizeinbits(base_order) - 1; i < NUMWORDS * 32; i++) {
				bitstr_clrbit(exp, i);
			}
		} while(bitstr_is_clear(exp));
		if(fis != null) try {
			fis.close();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}

/*
	private void get_random_exponent(int[] exp) {
		byte[] buffer = new byte[4 * NUMWORDS];
		do {
			get_random(buffer);
			bitstr_import(exp, buffer);
			for(int i = bitstr_sizeinbits(base_order) - 1; i < NUMWORDS * 32; i++) {
				bitstr_clrbit(exp, i);
			}
		} while(bitstr_is_clear(exp));
	}
*/

	protected abstract int get_symmetric_key_bytes();
	protected abstract void ctr_crypt(byte[] data, int len, final byte[] key);
	protected abstract void cbc_mac(byte[] mac, final byte[] data, int len, final byte[] key);
	protected abstract void davies_meyer(byte[] out, final byte[] in, int ilen);

	protected interface NonceListener {
		void set_nonce(byte[] nonce);
	}

	private NonceListener symmetric_crypt_nonce_listener = null;
	private int symmetric_crypt_nonce_length = 0;

	protected void set_symmetric_crypt_nonce_listener(NonceListener l, int nonce_len) {
		symmetric_crypt_nonce_listener = l;
		symmetric_crypt_nonce_length = nonce_len;
	}

	/* a non-standard KDF */
	private void kdf(byte[] k1, byte[] k2, final int[] Zx, final int[] Rx, final int[] Ry) {
		System.err.printf("method: org.rivoreo.crypto.ECIES::kdf(byte[%d], byte[%d], final int[%d], final int[%d], final int[%d])\n", k1.length, k2.length, Zx.length, Rx.length, Ry.length);
		int key_bytes = get_symmetric_key_bytes();
		int bufsize = (3 * (4 * NUMWORDS) + 1 + key_bytes - 1) & ~(key_bytes - 1);
		byte[] buffer = new byte[bufsize];
		byte[] kbuffer = new byte[key_bytes / 2];
		//memset(buffer, 0, bufsize);
		bitstr_export(buffer, 0, Zx);
		bitstr_export(buffer, 4 * NUMWORDS, Rx);
		bitstr_export(buffer, 8 * NUMWORDS, Ry);
		int i;
		buffer[12 * NUMWORDS] = 0;
		davies_meyer(kbuffer, buffer, bufsize / key_bytes);
		//debug_print_bytes(kbuffer, 8);
		for(i = 0; i < kbuffer.length; i++) k1[i] = kbuffer[i];
		buffer[12 * NUMWORDS] = 1;
		davies_meyer(kbuffer, buffer, bufsize / key_bytes);
		for(i = 0; i < kbuffer.length; i++) k1[kbuffer.length + i] = kbuffer[i];
		buffer[12 * NUMWORDS] = 2;
		davies_meyer(kbuffer, buffer, bufsize / key_bytes);
		for(i = 0; i < kbuffer.length; i++) k2[i] = kbuffer[i];
		buffer[12 * NUMWORDS] = 3;
		davies_meyer(kbuffer, buffer, bufsize / key_bytes);
		for(i = 0; i < kbuffer.length; i++) k2[kbuffer.length + i] = kbuffer[i];
	}

	public void encrypt(byte[] msg, final byte[] raw, final PublicKey pubkey) {
		if(msg.length < raw.length + OVERHEAD) throw new IllegalArgumentException(String.format("%d bytes needed for msg", raw.length + OVERHEAD));
		Stream stm = new Stream();
		encrypt_start(stm, msg, pubkey);
		//for(int i=0; i<raw.length; i++) msg[ECIES_START_OVERHEAD + i] = raw[i]; 
		//byte[] buffer = new byte[msg.length - ECIES_START_OVERHEAD];
		byte[] buffer = new byte[raw.length + ECIES_CHUNK_OVERHEAD];
		encrypt_chunk(stm, buffer, raw.length);
		for(int i=0; i<buffer.length; i++) msg[ECIES_START_OVERHEAD + i] = buffer[i];
	}

	public int decrypt(byte[] raw, int len, final byte[] msg, final PrivateKey privkey) {
		if(len > raw.length) throw new IllegalArgumentException("len > raw.length");
		if(msg.length != len + OVERHEAD) throw new IllegalArgumentException(String.format("Encrypted data must be %d bytes long.", len + OVERHEAD));
		Stream stm = new Stream();
		byte[] mac = new byte[ECIES_CHUNK_OVERHEAD];
		int r = decrypt_start(stm, msg, privkey);
		if(r < 0) return r;

		//byte[] buffer = new byte[msg.length - ECIES_START_OVERHEAD];
		cbc_mac(mac, sub_byte_array(msg, ECIES_START_OVERHEAD), len, stm.k2);
		if(compare_bytes(mac, sub_byte_array(msg, ECIES_START_OVERHEAD + len), ECIES_CHUNK_OVERHEAD) != 0) return -2;
	
		for(int i=0; i<len; i++) raw[i] = msg[ECIES_START_OVERHEAD + i];
		//for(int i = 0; i < msg.length - ECIES_START_OVERHEAD; i++) raw[i] = msg[ECIES_START_OVERHEAD + i];
		ctr_crypt(raw, len, stm.k1);
		return 1;
	}

	public int decrypt(byte[] raw, final byte[] msg, final PrivateKey privkey) {
		return decrypt(raw, raw.length, msg, privkey);
	}

	public final int ECIES_START_OVERHEAD;
	public final int ECIES_CHUNK_OVERHEAD;

	public class Stream {
		public Stream() {
			int key_bytes = get_symmetric_key_bytes();
			k1 = new byte[key_bytes];
			k2 = new byte[key_bytes];
		}
		private final byte[] k1, k2;
	}

	public void encrypt_start(Stream stm, byte[] msg, final PublicKey pubkey) {
		int[] Rx = new int[NUMWORDS];
		int[] Ry = new int[NUMWORDS];
		int[] Zx = new int[NUMWORDS];
		int[] Zy = new int[NUMWORDS];
		int[] k = new int[NUMWORDS];
		do {
			get_random_exponent(k);
			bitstr_load(Zx, pubkey.x, KEY_SIZE);
			bitstr_load(Zy, pubkey.y, KEY_SIZE);
			point_mult(Zx, Zy, k);
			point_double(Zx, Zy);	// cofactor h = 2 on all supported curves
		} while(point_is_zero(Zx, Zy));
		point_copy(Rx, Ry, base_x, base_y);
		point_mult(Rx, Ry, k);

		if(symmetric_crypt_nonce_listener != null && symmetric_crypt_nonce_length > 0) {
			byte[] buffer = new byte[symmetric_crypt_nonce_length];
			bitstr_dump(buffer, buffer.length, Ry);
			symmetric_crypt_nonce_listener.set_nonce(buffer);
		}

		kdf(stm.k1, stm.k2, Zx, Rx, Ry);

		bitstr_export(msg, 0, Rx);
		bitstr_export(msg, 4 * NUMWORDS, Ry);
	}

	public void encrypt_chunk(final Stream stm, byte[] msg, int len) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::generate_keys(Stream, byte[%d], %d)\n", msg.length, len);
		ctr_crypt(msg, len, stm.k1);
		byte[] mac = new byte[ECIES_CHUNK_OVERHEAD];
		cbc_mac(mac, msg, len, stm.k2);
		for(int i=0; i<mac.length; i++) msg[len + i] = mac[i];
	}

	public int decrypt_start(Stream stm, final byte[] msg, final PrivateKey privkey) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::decrypt_start(Stream, byte[%d], PrivateKey)\n", msg.length);
		int[] Rx = new int[NUMWORDS];
		int[] Ry = new int[NUMWORDS];
		int[] Zx = new int[NUMWORDS];
		int[] Zy = new int[NUMWORDS];
		int[] d = new int[NUMWORDS];
		bitstr_import(Rx, msg);
		bitstr_import(Ry, sub_byte_array(msg, 4 * NUMWORDS));
		if(internal_validate_pubkey(Rx, Ry) < 0) {
			System.err.println("debug: internal_validate_pubkey failed");
			return -1;
		}

		if(symmetric_crypt_nonce_listener != null && symmetric_crypt_nonce_length > 0) {
			byte[] buffer = new byte[symmetric_crypt_nonce_length];
			bitstr_dump(buffer, buffer.length, Ry);
			symmetric_crypt_nonce_listener.set_nonce(buffer);
		}

		bitstr_load(d, privkey.k, KEY_SIZE);
		point_copy(Zx, Zy, Rx, Ry);
		point_mult(Zx, Zy, d);
		point_double(Zx, Zy);	// cofactor h = 2 on all supported curves
		if(point_is_zero(Zx, Zy)) return -1;
		kdf(stm.k1, stm.k2, Zx, Rx, Ry);
		return 1;
	}

	public int decrypt_chunk(final Stream stm, byte[] msg, int len) {
		//System.err.printf("method: org.rivoreo.crypto.ECIES::decrypt_chunk(Stream, byte[%d], %d)\n", msg.length, len);
		byte[] mac = new byte[ECIES_CHUNK_OVERHEAD];
		cbc_mac(mac, msg, len, stm.k2);
		if(compare_bytes(mac, sub_byte_array(msg, len), ECIES_CHUNK_OVERHEAD) != 0) return -2;
		ctr_crypt(msg, len, stm.k1);
		return 1;
	}

/*
	public static void main(String[] a) {
		ECIES ecies = new ECIES(SECTDomainParameters.sect233r1);
		byte[] a = { (byte)0xad, (byte)0x90, (byte)0x8f, (byte)0x7d, (byte)0x5f, (byte)0x11, (byte)0xfe, (byte)0x81, (byte)0x42, (byte)0xce, (byte)0xe9, (byte)0x20, (byte)0x3b, (byte)0x33, (byte)0x3b, (byte)0x21, (byte)0x58, (byte)0xbb, (byte)0x23, (byte)0x09, (byte)0x8c, (byte)0x7f, (byte)0x2c, (byte)0x33, (byte)0x6c, (byte)0xde, (byte)0x7e, (byte)0x64, (byte)0x66 };
		int[] bs = new int[NUMWORDS];
		bitstr_import(bs, a);
	}
*/
}
