/* vi: set tabstop=4 nocindent noautoindent: */



#include "ssl_tool.h"


#ifdef  ENABLE_SSL


/**
@brief   SSL暗号用ライブラリ
@file    ssl_tool.c
@author  Fumi.Iseki (C)

@par ライブラリ
--L/usr/local/ssl/lib -lcrypto 


@par 転送データ（テキスト）形式
@code
通常転送データ：            [データ][\r][\n]
通常転送データ（複数行）：  [データ][\r][\n][データ][\r][\n]...[\r][\n][END][\r][\n]
暗号化時の転送データ：      [暗号化データ][\r][\n] （一行）
                                   |→復号→ [データ][\r][\n]....[\r][\n][END][\r][\n]

注）データを転送するとき，\r\n が生のデータの最後につくのか，暗号化されたデータの最後につくのか，よく注意しないといけない．
@endcode


@attention
このプログラムは openSSL を使用しています．@n
This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
*/



Buffer* Base64_DHspki  = NULL;
Buffer* Base64_RSAspki = NULL;


Buffer* CRYPT_SharedKey 	   = NULL;
const 	EVP_CIPHER* CRYPT_Type = NULL;
DH*	  	DHkey 				   = NULL;

int    	KEYEX_Algorism = 0;
int     CRYPT_Algorism = 0;


/*
typedef struct dh_st
{
	BIGNUM* p;
	BIGNUM* g;
	BIGNUM* pub_key;
	BIGNUM*	priv_key; 
} DH;
 */




///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//

/**
int  gen_CRYPT_SharedKey(int keyex, Buffer spki)

鍵交換アルゴリズム keyex 下で，共有鍵を計算する．

自分のSPKIと秘密鍵は計算済みでなければならない．
生成された共有鍵は Buffer* CRYPT_SharedKey に格納される．

@b keyex @n
@b SSL_SH: Diffie-Hellman の場合は，DH* DHkeyの秘密鍵が，DH_generate_key() （または 
gen_DHspki(), get_DHspki_fs(), get_DHspki_ff()でも良い）により計算（またはロード）され，
DH* DHkey に格納されていなければならない．@n
@b RSA: RSAの場合は,,,,,,,,,,,,@n

@param  keyex  鍵交換アルゴリズム．現在サポートしているのは SSL_DH のみ．
@param  spki   相手の SPKI 

@retval TRUE   成功
@retval FALSE  失敗

@see DH_generate_key()
*/
int  gen_CRYPT_SharedKey(int keyex, Buffer spki)
{
	if (spki.buf==NULL) return FALSE;

	KEYEX_Algorism = keyex;

	if (CRYPT_SharedKey!=NULL) del_Buffer(&CRYPT_SharedKey);
	CRYPT_SharedKey = new_Buffer();

	if	    (KEYEX_Algorism==SSL_DH)  *CRYPT_SharedKey = get_DHsharedkey(spki); // 共有かぎを得る
	else if (KEYEX_Algorism==SSL_RSA) *CRYPT_SharedKey = dup_Buffer(spki);		// 未実装
	else {
		KEYEX_Algorism  = 0;
		del_Buffer(&CRYPT_SharedKey);
		CRYPT_SharedKey = NULL;
		return FALSE;
	}

//	dump_Buffer(*CRYPT_SharedKey);
	return TRUE;
}




/**
int    has_CRYPT_SharedKey(void)

デフォルト共有鍵 CRYPT_SharedKey を持っているかどうかを返答する．

@retval TRUE   持っている．
@retval FALSE  持っていない．
*/
int    has_CRYPT_SharedKey(void)
{
	if (CRYPT_SharedKey==NULL)	  	return FALSE;
	if (CRYPT_SharedKey->buf==NULL) return FALSE;
	return  TRUE;
}




/**
void	clear_CRYPT_parameter()

暗号用パラメータを初期化する．

*/
void	clear_CRYPT_parameter()
{
	free_EVPAPI_Buffer();

	if (CRYPT_SharedKey!=NULL) {
		del_Buffer(&CRYPT_SharedKey);
		CRYPT_SharedKey = NULL;
	}

	CRYPT_Algorism  = 0;
	KEYEX_Algorism  = 0;
}









///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//

int    tcp_send_crypt_Buffer(int sock, Buffer* data, Buffer* key)
{
	int	 cc = -2;
	Buffer buf;

	if (data==NULL) return -1;

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			buf = encode_EVPAPI_Buffer(*data, *key);
			cc = tcp_send_Buffer(sock, &buf);
			free_Buffer(&buf);
		}
	}
	else {
		cc = tcp_send_Buffer(sock, data);
	}

	return cc;
}




int   udp_send_crypt_Buffer(int sock, Buffer* data,  struct sockaddr_in* sv, Buffer* key)
{
	int	 cc = -2;
	Buffer buf;

	if (data==NULL) return -1;

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			buf = encode_EVPAPI_Buffer(*data, *key);
			cc = udp_send_Buffer(sock, &buf, sv);
			free_Buffer(&buf);
		}
	}
	else {
		cc = udp_send_Buffer(sock, data, sv);
	}

	return cc;
}




int  tcp_recv_crypt_Buffer(int sock, Buffer* data, Buffer* key)
{  
    Buffer  buf;

	buf = make_Buffer(MAXBUFSZ);
	int cc = tcp_recv_Buffer(sock, &buf);
	if (cc<=0) {
		free_Buffer(&buf);
		return cc;
	}
   
    if (CRYPT_Algorism) {
        if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
        	Buffer dec = decode_EVPAPI_Buffer(buf, *key);
			copy_Buffer(&dec, data);
        	free_Buffer(&dec);
		}
		else return -2;
    }
	else {
		copy_Buffer(&buf, data);
	}

	cc = data->vldsz;
	free_Buffer(&buf);
    return cc;
}




int  udp_recv_crypt_Buffer(int sock, Buffer* data, struct sockaddr_in* sv, Buffer* key)
{  
    Buffer  buf;

	buf = make_Buffer(MAXBUFSZ);
	int cc = udp_recv_Buffer(sock, &buf, sv);
	if (cc<=0) {
		free_Buffer(&buf);
		return cc;
	}
   
    if (CRYPT_Algorism) {
        if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
        	Buffer dec = decode_EVPAPI_Buffer(buf, *key);
			copy_Buffer(&dec, data);
        	free_Buffer(&dec);
		}
		else return -2;
    }
	else {
		copy_Buffer(&buf, data);
	}

	cc = data->vldsz;
	free_Buffer(&buf);
    return cc;
}




Buffer  get_plain_Buffer(Buffer data, Buffer* key)
{
	Buffer buf = init_Buffer();

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			buf = decode_EVPAPI_Buffer(data, *key);
		}
	}
	return buf;
}




Buffer  get_crypt_Buffer(Buffer data, Buffer* key)
{
	Buffer buf = init_Buffer();

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			buf = encode_EVPAPI_Buffer(data, *key);
		}
	}
	return buf;
}




/**
int    tcp_send_crypt_mesg(int sock, char* mesg, Buffer* key)

暗号化対応の転送関数．

暗号化データの最後には必ず @\r@\n がつく．生データの最後の @\r@\n はユーザの責任で付加する（通常はつける）．
CRYPT_Algorism が 0 でない場合には暗号化(+@\r@\n)して送られる．

tcp_send_crypt_mesgln(), tcp_send_crypt_sBufferln() は最後の改行コードの
取り扱いが混乱するので定義しない方が良い（と思う）．定義する場合は tcp_send_mesgln(),
tcp_send_sBufferln()とは使い方が異なることになるので注意が必要．

@param  sock   ソケット
@param  mesg   転送するデータ．
@param  key    暗号鍵へのポインタ．NULLの場合はデフォルト鍵（CRYPT_SharedKey）を使う．

@retval 0以上  転送データ数
@retval 0未満  通信エラー

@code
tcp_send_crypt_mesg(sock, "OK\r\n");
@endcode
*/
int    tcp_send_crypt_mesg(int sock, char* mesg, Buffer* key)
{
	int	 cc = -2;
	Buffer buf, enc;

	if (mesg==NULL) return -1;
	DEBUG_ESCP(" SEND -> [%s]\n", mesg);

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			buf = make_Buffer_bystr(mesg);
			enc = encode_EVPAPI_Buffer(buf, *key);
			free_Buffer(&buf);
			if (enc.vldsz>0) {
				buf = encode_base64_Buffer(enc);
				free_Buffer(&enc);

				DEBUG_ESCP("CSEND -> [%s]\n", (char*)buf.buf);
				cc = tcp_send_sBufferln(sock, &buf);
				free_Buffer(&buf);
			}
		}
	}
	else {
		cc = tcp_send_mesg(sock, mesg);
	}

	return cc;
}




/**
int    tcp_send_crypt_sBuffer(int sock, Buffer* mesg, Buffer* key)

暗号化対応の転送関数．

暗号化データの最後には必ず @\r@\n がつく．生データの最後の @\r@\n はユーザの責任で付加する（通常はつける）．
CRYPT_Algorism が 0 でない場合には暗号化(+@\r@\n)して送られる．

tcp_send_crypt_mesgln(), tcp_send_crypt_sBufferln() は最後の改行コードの
取り扱いが混乱するので定義しない方が良い（と思う）．定義する場合は tcp_send_mesgln(),
tcp_send_sBufferln()とは使い方が異なることになるので注意が必要．

@param  sock   ソケット
@param  mesg   転送するデータ．
@param  key    暗号鍵へのポインタ．NULLの場合はデフォルト鍵（CRYPT_SharedKey）を使う．

@retval 0以上  転送データ数
@retval 0未満  通信エラー

@code
tcp_send_crypt_mesg(sock, "OK\r\n");
@endcode
*/
int    tcp_send_crypt_sBuffer(int sock, Buffer* mesg, Buffer* key)
{
	int	 cc = -2;
	Buffer buf, enc;

	if (mesg==NULL) return -1;
	DEBUG_ESCP(" SEND -> [%s]\n", (char*)mesg->buf);

	if (CRYPT_Algorism) {
		//
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			enc = encode_EVPAPI_Buffer(*mesg, *key);
			if (enc.vldsz>0) {
				buf = encode_base64_Buffer(enc);
				free_Buffer(&enc);

				DEBUG_ESCP("CSEND -> [%s]\n", (char*)buf.buf);
				cc = tcp_send_sBufferln(sock, &buf);
				free_Buffer(&buf);
			}
		}
	}
	else {
		cc = tcp_send_sBuffer(sock, mesg);
	}

	return cc;
}




/**
Buffer  get_plain_message(char*  mesg, Buffer* key)

復号化関数．復号の前に Base64のコードを行なう．

@b CRYPT_Algorism が 0 の場合は変換を行なわない． 

@param  mesg  変換するメッセージ
@param  key   暗号鍵へのポインタ．NULLの場合はデフォルト鍵（@b CRYPT_SharedKey）を使う．

@return 変換されたメッセージ
*/
Buffer  get_plain_message(char* mesg, Buffer* key)
{
	Buffer  buf, dec;

	buf = make_Buffer_bystr(mesg);
	DEBUG_ESCP(" MESG -> [%s]\n", mesg);

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			dec = decode_base64_Buffer(buf);
			if (dec.vldsz>0) {
				free_Buffer(&buf);
				buf = decode_EVPAPI_Buffer(dec, *key);
				free_Buffer(&dec);
				DEBUG_ESCP("DMESG -> [%s]\n", (char*)buf.buf);
			}
		}
	}
	return buf;
}





/**
Buffer  get_plain_sBuffer(Buffer mesg, Buffer* key)

復号化関数．復号の前に Base64のコードを行なう．

@b CRYPT_Algorism が 0 の場合は変換を行なわない． 

@param  mesg  変換するメッセージ
@param  key   暗号鍵へのポインタ．NULLの場合はデフォルト鍵（@b CRYPT_SharedKey）を使う．

@return 変換されたメッセージ
*/
Buffer  get_plain_sBuffer(Buffer mesg, Buffer* key)
{
	Buffer buf, dec;

	buf = dup_Buffer(mesg);
	DEBUG_ESCP(" MESG -> [%s]\n", (char*)mesg.buf);

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			dec = decode_base64_Buffer(buf);
			if (dec.vldsz>0) {
				free_Buffer(&buf);
				buf = decode_EVPAPI_Buffer(dec, *key);
				free_Buffer(&dec);
				DEBUG_ESCP("DMESG -> [%s]\n", (char*)buf.buf);
			}
		}
	}
	return buf;
}




/**
Buffer  get_crypt_message(char*  mesg, Buffer* key)

暗号化関数．暗号化に加えて Base64符号化も行なう．

@b CRYPT_Algorism が 0 の場合は変換を行なわない． 

@param  mesg  変換するメッセージ
@param  key   暗号鍵へのポインタ．NULLの場合はデフォルト鍵（@b CRYPT_SharedKey）を使う．

@return 変換されたメッセージ
*/
Buffer  get_crypt_message(char* mesg, Buffer* key)
{
	Buffer  buf, enc;

	buf = make_Buffer_bystr(mesg);

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			enc = encode_EVPAPI_Buffer(buf, *key);
			if (enc.vldsz>0) {
				free_Buffer(&buf);
				buf = encode_base64_Buffer(enc);
				free_Buffer(&enc);
			}
		}
	}
	return buf;
}




/**
Buffer  get_crypt_sBuffer(Buffer mesg, Buffer* key)

復号化関数．暗号化に加えて Base64符号化も行なう．

@b CRYPT_Algorism が 0 の場合は変換を行なわない． 

@param  mesg  変換するメッセージ
@param  key   暗号鍵へのポインタ．NULLの場合はデフォルト鍵（@b CRYPT_SharedKey）を使う．

@return 変換されたメッセージ
*/
Buffer  get_crypt_sBuffer(Buffer mesg, Buffer* key)
{
	Buffer  buf, enc;

	buf = dup_Buffer(mesg);

	if (CRYPT_Algorism) {
		if (key==NULL) key = CRYPT_SharedKey;
		if (key!=NULL) {
			enc = encode_EVPAPI_Buffer(buf, *key);
			if (enc.vldsz>0) {
				free_Buffer(&buf);
				buf = encode_base64_Buffer(enc);
				free_Buffer(&enc);
			}
		}
	}
	return buf;
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Client's Side Check 
//

/**
int    check_server_spki(char* filename, Buffer ipaddr, Buffer spki)

サーバから得た SPKIを，以前保存しておいた SPKIと比較して，サーバの正当性を検査する．

ファイル中にSPKIが無い場合は，新規のサーバとして，IPアドレスとSPKIを保存する．
クライアント側でサーバの認識に使用する．

@param  filename  鍵が保存されているファイル
@param  ipaddr    サーバのIPアドレス（文字列型）例：192.168.1.1
@param  spki      サーバのSPKI(DER)．

@retval TRUE      サーバのSPKIは以前保存したSPKIに一致した．または新規のサーバ．
@retval FALSE     サーバのSPKIは以前保存したSPKIに一致しない．
 */
int    check_server_spki(Buffer ipaddr, Buffer spki, char* filename)
{
	Buffer  buf;
	FILE*	fp;

	buf = init_Buffer();

	fp = fopen(filename, "rb");
	if (fp!=NULL) {
		buf = read_spki_with_ipaddr(ipaddr, fp);
		fclose(fp);
		if (buf.buf!=NULL) {
			if (bincmp_Buffer(buf, spki)) {
				free_Buffer(&buf);
				return FALSE;
			}
		}
	}

	if (buf.buf==NULL) {
		fp = file_chmod_open(filename, (char*)"a", S_IRUSR | S_IWUSR);
		save_spki_with_ipaddr(ipaddr, spki, fp);
		fclose(fp);
	}

	return TRUE;
}





/**
int    save_spki_with_ipaddr(Buffer ipa, Buffer pki, FILE* fp)

サーバの公開鍵情報（X.509の SubjectPublicKeyInfo）を識別子ipa と共にファイルに保存する．

識別子には通常は IPアドレスが使用される．@n
識別子，鍵情報の順で保存される．既にファイルが存在する場合はデータはファイルの最後に追加される．@n
識別子にIPアドレスを使用することにより，クライアント側でのサーバ認証に使用する．

@param  ipa   識別子．通常はサーバのIPアドレス．
@param  pki   保存する鍵情報（DER）．
@param  fp    保存先のファイル記述子

@retval TRUE  成功．
@retval FALSE 失敗．
*/
int    save_spki_with_ipaddr(Buffer ipa, Buffer pki, FILE* fp)
{	
	unsigned int  md;

	if (fp==NULL) return FALSE;

	md = FBRTL_IPADDRESS | FBRTL_STRING;
	if (!save_taggedBuffer(ipa, fp, md, FALSE)) return FALSE;

	md = FBRTL_SPKI | FBRTL_ORIGINAL;
	if (!save_taggedBuffer(pki, fp, md, FALSE)) return FALSE;

	return  TRUE;
}




/**
Buffer  read_spki_with_ipaddr(Buffer ipa, FILE* fp)

IPアドレス ipaを持つサーバの公開鍵情報（X.509の SubjectPublicKeyInfo）をファイルから読み込む．

@param  ipa  検索するIPアドレス
@param  fp   読み込むファイル記述子

@return IPアドレスipa の鍵情報(DER)．
*/
Buffer  read_spki_with_ipaddr(Buffer ipa, FILE* fp)
{
	unsigned int md;
	Buffer	ips, pki;

	pki = init_Buffer();
	if (ipa.buf==NULL) return pki;

	md  = FBRTL_IPADDRESS | FBRTL_STRING;

	ips = read_taggedBuffer(fp, &md);
	while (!feof(fp) && strcmp_Buffer(ipa, ips)) {
		ips = read_taggedBuffer(fp, &md);
	}
		
	if (!strcmp_Buffer(ipa, ips)) {
		md  = FBRTL_SPKI | FBRTL_ORIGINAL;	
		pki = read_taggedBuffer(fp, &md);
	}
	free_Buffer(&ips);

	return pki;
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// EVP API
//

/**
int    init_EVPAPI_Buffer(int type)

共通鍵暗号のアルゴリズムを指定する．

一度に（同時に）複数の暗号化オブジェクトは指定できない．
設定をクリアするには free_EVPAPI_Buffer()を使用する．

@param  type   暗号化オブジェクト（暗号化手法）

@retval TRUE   成功．
@retval FALSE  失敗．

OpenSSLの暗号化オブジェクトの種類（一部）．
@code
- AES:      EVP_aes_#_X()  #は 128, 192, 256  Xは ecb, cbc, cfb, ofb  例：EVP_aes_128_cbc()
- Blowfish: EVP_bf_X()  Xは ecb, cbc, cfb, ofb
- DES:      EVP_des_X() Xは ecb, cbc, cfb, ofb
- 3DES:     EVP_des_ede(),  EVP_des_ede_X()    Xは cbc, cfb, ofb
            EVP_des_ede3(), EVP_des_ede3_X()   Xは cbc, cfb, ofb
- RC4:      EVP_rc4(), EVP_rc4_40()
@endcode
*/
int    init_EVPAPI_Buffer(int type)
{
	if (type==SSL_AES128CBC) {
		CRYPT_Type	 	= EVP_aes_128_cbc();
		CRYPT_Algorism 	= SSL_AES128CBC;
	}
	else if (type==SSL_3DES3CBC) {
		CRYPT_Type 		= EVP_des_ede3_cbc();
		CRYPT_Algorism 	= SSL_3DES3CBC;
	}
	else {
		CRYPT_Type	   = NULL;
		CRYPT_Algorism = 0;
		return FALSE;
	}

	return TRUE;
}




/**
void free_EVPAPI_Buffer(void)

暗号化パラメータをクリアする．

*/
void free_EVPAPI_Buffer(void)
{
	CRYPT_Type	   = NULL;
	CRYPT_Algorism = 0;
}





/**
Buffer  decode_EVPAPI_Buffer(Buffer buf, Buffer shkey)

init_EVPAPI_Buffer() によって指定された方法により 暗号文bufを復号する．

@param  buf    復号する暗号データ
@param  shkey  共有キー
@return 復号文（平文）
*/
Buffer  decode_EVPAPI_Buffer(Buffer buf, Buffer shkey)
{
	int		sz, ss;
	Buffer dec;
	EVP_CIPHER_CTX* ctx;
	unsigned char*  iv;

	dec = init_Buffer();
	if (CRYPT_SharedKey==NULL || CRYPT_Type==NULL) return dec;

	iv  = (unsigned char*)&(shkey.buf[shkey.vldsz - SSL_IV_SIZE]);

	ctx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
	EVP_CIPHER_CTX_init(ctx);
	//EVP_DecryptInit_ex(ctx, CRYPT_Type, NULL, shkey.buf, NULL);
	EVP_DecryptInit_ex(ctx, CRYPT_Type, NULL, shkey.buf, iv);

	dec = make_Buffer(buf.vldsz + EVP_CIPHER_CTX_block_size(ctx) + 1);
	if (dec.buf==NULL) return dec;

	EVP_DecryptUpdate(ctx, dec.buf, &sz, buf.buf, buf.vldsz);
	EVP_DecryptFinal_ex(ctx, &(dec.buf[sz]), &ss);
	dec.vldsz = sz + ss;
	dec.buf[dec.vldsz] = '\0';

	EVP_CIPHER_CTX_cleanup(ctx);
	free(ctx);
	return dec;
}





/**
Buffer  encode_EVPAPI_Buffer(Buffer buf, Buffer shkey)

init_EVPAPI_Buffer() によって指定された方法により buf を暗号化する．

鍵は @b CRYPT_SharedKey を使う．
暗号化は @b SSL_ENC_BLCKSZ Byte毎に，暗号化される（ブロック暗号）

@param  buf    暗号化するデータ
@param  shkey  共有キー
@return 暗号文
*/
Buffer  encode_EVPAPI_Buffer(Buffer buf, Buffer shkey)
{
	int		i, len, ss=0, sz;
	Buffer enc;
	EVP_CIPHER_CTX* ctx;
	unsigned char*  iv;

	enc = init_Buffer();
	if (CRYPT_SharedKey==NULL || CRYPT_Type==NULL) return enc;

	iv  = (unsigned char*)&(shkey.buf[shkey.vldsz - SSL_IV_SIZE]);

	ctx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
	EVP_CIPHER_CTX_init(ctx);
	//EVP_EncryptInit_ex(ctx, CRYPT_Type, NULL, shkey.buf, NULL);
	EVP_EncryptInit_ex(ctx, CRYPT_Type, NULL, shkey.buf, iv);

	len = buf.vldsz;
	enc = make_Buffer(len + EVP_CIPHER_CTX_block_size(ctx));
	if (enc.buf==NULL) return enc;

	for (i=0; i<len/SSL_ENC_BLCKSZ; i++) {
		EVP_EncryptUpdate(ctx, &(enc.buf[ss]), &sz, &(buf.buf[i*SSL_ENC_BLCKSZ]), SSL_ENC_BLCKSZ);
		ss += sz;
	}
	if (len%SSL_ENC_BLCKSZ!=0) {
		EVP_EncryptUpdate(ctx, &(enc.buf[ss]), &sz, &(buf.buf[i*SSL_ENC_BLCKSZ]), len%SSL_ENC_BLCKSZ);
		ss += sz;
	}
	EVP_EncryptFinal_ex(ctx, &(enc.buf[ss]), &sz);
	enc.vldsz = ss + sz;

	EVP_CIPHER_CTX_cleanup(ctx);
	free(ctx);
	return  enc;
}






///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Diffie-Hellman 鍵交換法
//

/**
int    save_DHspki_with_private(Buffer pki, FILE* fp)

Diffie-Hellman の公開鍵情報（X.509の SubjectPublicKeyInfo）pkiとプライベート鍵
(大域変数DHkeyより取得)をファイルに保存する．公開鍵情報，プライベート鍵の順で
書き込まれる．既にファイルが存在する場合，その内容は上書きされる．

@param  pki   保存する鍵情報（DER）．
@param  fp    ファイルポインタ 

@retval TRUE  成功．
@retval FALSE 失敗．ファイルの内容は保証されない．
*/
int    save_DHspki_with_private(Buffer pki, FILE* fp)
{	
	unsigned int  md;
	Buffer pv;

	if (fp==NULL || DHkey==NULL) return FALSE;

	md = FBRTL_SPKI | FBRTL_ORIGINAL;
	if (!save_taggedBuffer(pki, fp, md, FALSE)) return FALSE;

	pv = get_DHprivatekey(DHkey);
	if (pv.buf==NULL) return FALSE;

	md = FBRTL_PRIV_KEY | FBRTL_ORIGINAL;
	if (!save_taggedBuffer(pv,  fp, md, FALSE)) return FALSE;
	free_Buffer(&pv);

	return  TRUE;
}




/**
Buffer  read_DHspki_with_private(FILE* fp)

Diffie-Hellman の公開鍵情報（X.509の SubjectPublicKeyInfo）とプライペート鍵をファイルから読み込む．

@param  fp  ファイルポインタ 
@return 読み込んだ鍵情報．
*/
Buffer  read_DHspki_with_private(FILE* fp)
{
	int  n, code;
	DH*  dh=NULL;
	unsigned int  md;
	Buffer	pp, pv, pk, gk, yk;

	pp = init_Buffer();
	if (fp==NULL) return pp;

	md = FBRTL_SPKI | FBRTL_ORIGINAL;
	pp = read_taggedBuffer(fp, &md);
	if (pp.buf==NULL) return pp;

	dh = DH_new();
	if (dh==NULL) {
		free_Buffer(&pp);
		return pp;
	}
	
	pk = get_DHPkey(pp);
	gk = get_DHGkey(pp);
	yk = get_DHYkey(pp);

	dh->p = BN_bin2bn((const unsigned char*)(pk.buf), pk.vldsz, NULL);
	dh->g = BN_bin2bn((const unsigned char*)(gk.buf), gk.vldsz, NULL);
	dh->pub_key = BN_bin2bn((const unsigned char*)(yk.buf), yk.vldsz, NULL);

	free_Buffer(&pk);
	free_Buffer(&gk);
	free_Buffer(&yk);

	// 鍵のチェック
	n = DH_check(dh, &code);
	if (n!=1 || code!=0) {
		DH_free(dh);
		free_Buffer(&pp);
		return pp;
	}

	// Private KEY の読み込み
	md = FBRTL_PRIV_KEY | FBRTL_ORIGINAL;
	pv = read_taggedBuffer(fp, &md);
	if (pv.buf==NULL) return pp;

	dh->priv_key = BN_bin2bn((const unsigned char*)(pv.buf), pv.vldsz, NULL);
	free_Buffer(&pv);

	if (DHkey!=NULL) DH_free(DHkey);
	DHkey = dh;

	return  pp;
}




/**
Buffer  get_DHspki_ff(char* filename, int ks)

Generate DH SPKI from File.

ファイルから Diffie-Hellman の鍵を読み込む．

ただし以下の場合にはksのサイズの鍵を新たに生成し，ファイルに保存してから返す．
-# ファイルが存在しないか，鍵を読み込めない
-# ファイルにプライベート鍵が保存されていない
-# ファイルの中の鍵の長さが ksより短い


大域変数 DH* DHkeyに DHの鍵パラメータが入る．

@param  filename  鍵が保存されているファイル
@param  ks        新たに生成する場合の鍵長(Bit)．

@return DER形式の公開鍵情報．
*/
Buffer  get_DHspki_ff(char* filename, int ks)
{
	Buffer  pki;
	FILE*	fp;

	pki = init_Buffer();
	if (filename==NULL || DHkey==NULL) return pki;

	if (file_exist(filename)) {
		DEBUG_MESG("Load DH public key. ");
		fp = fopen(filename, "rb");
		pki = read_DHspki_with_private(fp);
		fclose(fp);
		DEBUG_MESG("... done.\n");
		if (pki.buf!=NULL) {
			if (DH_size(DHkey)<(ks+7)/8 || DHkey->priv_key==NULL) free_Buffer(&pki);
		}
	}

	if (pki.buf==NULL) {
		DEBUG_MESG("Generate DH public key.\n");
		pki = gen_DHspki(ks);
		DEBUG_MESG("... done.\n");

		fp = file_chmod_open(filename, (char*)"w", S_IRUSR | S_IWUSR);
		if (fp!=NULL) {
			save_DHspki_with_private(pki, fp);
			fclose(fp);
		}
	}

	/*
	DEBUG_MODE {
		Buffer enc;
		enc = encode_base64_Buffer(pki);
		DEBUG_MESG("SPKI = [%s]\n", enc.buf);
		free_Buffer(&enc);
	}*/
	return pki;
}




/**
Buffer  gen_DHspki(int ks)

Diffie-Hellman の鍵を生成し,公開鍵の完全な情報（X.509の SubjectPublicKeyInfo）のDER形式で返す．@n
大域変数 DH* DHkeyに DHの鍵パラメータが入る．

@param  ks  鍵長(Bit)．512か 1024を指定．
@return DER形式の公開鍵情報．
*/
Buffer  gen_DHspki(int ks)
{
	int	   sz, n, code;
	Buffer px, pp, pk;

	pk = init_Buffer();

	//if (!RAND_load_file("/dev/random", 1024)) return pk;
	DEBUG_MESG("Load /dev/urandom.");
	if (!RAND_load_file("/dev/urandom", 1024)) return pk;

	do {
		DEBUG_MESG(" Generate parameters.");
		if (DHkey!=NULL) DH_free(DHkey);
		DHkey = DH_generate_parameters(ks, DH_GENERATOR_2, NULL, NULL);
		n = DH_check(DHkey, &code);
	} while (n!=1 || code!=0);

	DEBUG_MESG(" Generate key.");
	sz = DH_generate_key(DHkey);				// 公開鍵(DH->pub_key)と秘密鍵(DH->priv_key)の生成
	if (sz==0) {
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}

	DEBUG_MESG(" Check key size.");
	sz = i2d_DHparams(DHkey, NULL);			 	// パラメタのサイズを検査 
	pp = px = make_Buffer(sz);				  	// パラメタを入れるメモリを確保 
	if (pp.buf==NULL) {
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}
	pp.vldsz = i2d_DHparams(DHkey, &(px.buf));  // パラメタ(P,G鍵)を DER形式へ．pp.bufに格納． 
												// px.bufは破壊される．

//	sz = DH_size(DHkey);
	sz = BN_num_bytes(DHkey->pub_key);
	px = make_Buffer(sz);
	if (px.buf==NULL) {
		DH_free(DHkey);
		DHkey = NULL;
		free_Buffer(&pp);
		return pk;
	}

//	for (i=0; i<sz; i++) px.buf[i] = ((unsigned char*)(DHkey->pub_key->d))[sz-i-1];
	px.vldsz = BN_bn2bin(DHkey->pub_key, px.buf);

	pk = join_DHpubkey(pp, px);					// pp -> DHパラメタ(P,G鍵),  px-> Y鍵

	free_Buffer(&pp);
	free_Buffer(&px);

	return pk;
}





/**
Buffer  gen_DHspki_fs(Buffer pki)

Generate DH SPKI from Server's SPKI.

サーバの公開鍵の情報（X.509の SubjectPublicKeyInfo, DER形式）の中の P,G鍵から
自分の公開鍵情報（X.509 SPKI, DER形式）を作り出す．@n
大域変数 DH* DHkeyに DHの鍵パラメータが入る．

@param  pki 相手の公開鍵情報（DER形式）
@return 自分の公開鍵の情報（X.509の SubjectPublicKeyInfo, DER形式）
*/
Buffer  gen_DHspki_fs(Buffer pki)
{
	int	   sz, n, code;
	Buffer px, pp, pk;
	Buffer pkey, gkey;

	//
	pk = init_Buffer();
	//
	if (DHkey!=NULL) DH_free(DHkey);
	DHkey = DH_new();
	if (DHkey==NULL) return pk;

	pkey = get_DHPkey(pki);
	gkey = get_DHGkey(pki);
	if (pkey.buf==NULL || gkey.buf==NULL) {
		free_Buffer(&pkey);
		free_Buffer(&gkey);
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}

	//
	DHkey->p = BN_bin2bn((const unsigned char*)(pkey.buf), pkey.vldsz, NULL);
	DHkey->g = BN_bin2bn((const unsigned char*)(gkey.buf), gkey.vldsz, NULL);
	free_Buffer(&pkey);
	free_Buffer(&gkey);

	//
	n = DH_check(DHkey, &code);
	if (n!=1 || code!=0) {
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}

	sz = DH_generate_key(DHkey);
	if (sz==0) {
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}

	//
	sz = i2d_DHparams(DHkey, NULL);			 		// パラメタのサイズを検査 
	pp = px = make_Buffer(sz);				  		// パラメタを入れるメモリを確保 
	if (pp.buf==NULL) {
		DH_free(DHkey);
		DHkey = NULL;
		return pk;
	}
	pp.vldsz = i2d_DHparams(DHkey, &(px.buf));  	// パラメタ(P,G鍵)を DER形式へ．pp.bufに格納． 
													// px.bufは破壊される．
	sz = BN_num_bytes(DHkey->pub_key);
	px = make_Buffer(sz);
	if (px.buf==NULL) {
		DH_free(DHkey);
		DHkey = NULL;
		free_Buffer(&pp);
		return px;
	}
	px.vldsz = BN_bn2bin(DHkey->pub_key, px.buf);	// 公開鍵(Y鍵)の DER形式 

	pk = join_DHpubkey(pp, px);						// pp -> DHパラメタ(P,G鍵),  px-> Y鍵

	free_Buffer(&pp);
	free_Buffer(&px);

	return pk;
}





/**
Buffer get_DHsharedkey(Buffer dec)

相手の公開鍵の情報（X.509の SubjectPublicKeyInfo, DER形式）とDHkey（自分の鍵情報）を元に共通かぎを作り出す．

大域変数 DH* DHkey を使用．DHkey には DH_generate_key(DHkey)（gen_DHspki() または 
gen_DHspki_fs()でも可）によって全てのパラメータ（P,G,Y,秘密鍵）が設定されていなければならない． 

@param  dec  相手の公開鍵情報
@return 共通鍵（バイナリ）

	参考：man -M /usr/local/ssl/man bn
*/
Buffer get_DHsharedkey(Buffer dec)
{
	Buffer  ykey, skey;
	
	skey = init_Buffer();
	ykey = get_DHYkey(dec);
	if (ykey.buf==NULL) return skey;

	skey = get_DHsharedkey_fY(ykey);
	
	free_Buffer(&ykey);
	return skey;
}







/**
Buffer get_DHsharedkey_fY(Buffer ykey)

相手の（Diffie-Hellman の）Y鍵とDHkey（自分の鍵情報）を元に共通鍵を作り出す．

  大域変数 DHkey を使用． DHkey には DH_generate_key()（gen_DHspki() または 
  gen_DHspki_fs()でも可）によって全てのパラメータ（P,G,Y,秘密鍵）が設定されて
  いなければならない． 

@param  ykey  相手の Y鍵（公開鍵）
@return 共通鍵（バイナリ）

	参考：man -M /usr/local/ssl/man bn
*/
Buffer get_DHsharedkey_fY(Buffer ykey)
{
	int sz, n;
	Buffer  buf;
	BIGNUM* yk;

	buf = init_Buffer();
	if (DHkey==NULL) return buf;

	yk = BN_bin2bn((const unsigned char*)(ykey.buf), ykey.vldsz, NULL);

	sz  = DH_size(DHkey);
	buf = make_Buffer(sz);
	n	= DH_compute_key((unsigned char*)buf.buf, yk, DHkey);
	buf.vldsz = sz;

	BN_free(yk);
	return buf;
}




/**
Buffer	get_DHYkey(Buffer param)

X.509の SubjectPublicKeyInfo のDER形式から，Diffie-Hellman のY鍵を取り出す． 

@param  param  X.509の SubjectPublicKeyInfo のDER形式
@return Y鍵（バイナリ）

参考：
@code
	[SEQ  [SEQ	[OBJ  Algor]
				[SEQ  [INT  P-key]
					  [INT  G-key]
					  [INT  P-keysize - 1]
				]
		  ]
		  [BIT  Seed(0x00)  [INT  Y-key]]
	]
@endcode
*/
Buffer	get_DHYkey(Buffer param)
{
	int	 i, lp, sz=0;
	Buffer  pp;
	pp = init_Buffer();

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;
	sz = sz + lp;

	sz = skip_DERtag(param, ASN1_BIT, sz, &lp);
	if (sz<0) return pp;
	sz++;						// for Seed(0x00)

	sz = skip_DERtag(param, ASN1_INT, sz, &lp);
	if (sz<0) return pp;

	pp = make_Buffer(lp);
	if (pp.buf==NULL) return pp;
	for (i=0; i<lp; i++) pp.buf[i] = param.buf[sz+i];
	pp.vldsz = lp;
	return pp;
}




/**
Buffer	get_DHPkey(Buffer param)

X.509の SubjectPublicKeyInfo のDER形式から，Diffie-Hellman のP鍵を取り出す． 

@param  param  X.509の SubjectPublicKeyInfo のDER形式
@return P鍵（バイナリ）

参考：
@code
	[SEQ  [SEQ	[OBJ  Algor]
				[SEQ  [INT  P-key]
					  [INT  G-key]
					  [INT  P-keysize - 1]
				]
		  ]
		  [BIT  Seed(0x00)  [INT  Y-key]]
	]
@endcode
*/
Buffer	get_DHPkey(Buffer param)
{
	int	 i, lp, sz=0;
	Buffer  pp;
	pp = init_Buffer();

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_OBJ, sz, &lp);
	if (sz<0) return pp;
	sz = sz + lp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_INT, sz, &lp);
	if (sz<0) return pp;

	pp = make_Buffer(lp);
	if (pp.buf==NULL) return pp;
	for (i=0; i<lp; i++) pp.buf[i] = param.buf[sz+i];
	pp.vldsz = lp;
	return pp;
}



	

/**
Buffer	get_DHGkey(Buffer param)

X.509の SubjectPublicKeyInfo のDER形式から，Diffie-Hellman のG鍵を取り出す． 
普通は 2 か 5．

@param  param  X.509の SubjectPublicKeyInfo のDER形式
@return G鍵（バイナリ）

参考：
@code
	[SEQ  [SEQ	[OBJ  Algor]
		  		[SEQ  [INT  P-key]
					  [INT  G-key]
					  [INT  P-keysize - 1]
		  		]
		  ]
		  [BIT  Seed(0x00)  [INT  Y-key]]
	] 
@endcode
*/
Buffer	get_DHGkey(Buffer param)
{
	int	 i, lp, sz=0;
	Buffer  pp;
	pp = init_Buffer();

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_OBJ, sz, &lp);
	if (sz<0) return pp;
	sz = sz + lp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_INT, sz, &lp);
	if (sz<0) return pp;
	sz = sz + lp;

	sz = skip_DERtag(param, ASN1_INT, sz, &lp);
	if (sz<0) return pp;

	pp = make_Buffer(lp);
	if (pp.buf==NULL) return pp;
	for (i=0; i<lp; i++) pp.buf[i] = param.buf[sz+i];
	pp.vldsz = lp;
	return pp;
}





/**
Buffer	get_DHalgor(Buffer param)

X.509の SubjectPublicKeyInfo のDER形式から，アルゴリズム(Algor)を取り出す． 

@param  param  X.509の SubjectPublicKeyInfo のDER形式
@return アルゴリズム（バイナリ）

参考：
@code
	[SEQ  [SEQ  [OBJ  Algor]
		  		[SEQ  [INT  P-key]
					  [INT  G-key]
					  [INT  P-keysize - 1]
		  		]
		  ]
		  [BIT  Seed(0x00)  [INT  Y-key]]
	]  
@endcode
*/
Buffer	get_DHalgor(Buffer param)
{
	int	 i, lp, sz=0;
	Buffer  pp;
	pp = init_Buffer();

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_SEQ, sz, &lp);
	if (sz<0) return pp;

	sz = skip_DERtag(param, ASN1_OBJ, sz, &lp);
	if (sz<0) return pp;

	pp = make_Buffer(lp);
	if (pp.buf==NULL) return pp;
	for (i=0; i<lp; i++) pp.buf[i] = param.buf[sz+i];
	pp.vldsz = lp;
	return pp;
}




/**
Buffer  get_DHprivatekey(DH* dh)

DH鍵 dh からプライベート鍵を取り出して返す．

@param  dh  DH鍵．
@return プライベート鍵
*/
Buffer  get_DHprivatekey(DH* dh)
{
	int	sz;
	Buffer pv;

	sz = BN_num_bytes(dh->priv_key);
	pv = make_Buffer(sz);
	if (pv.buf==NULL) return pv;
	pv.vldsz = BN_bn2bin(dh->priv_key, pv.buf);

	return pv;
}




/**
Buffer	join_DHpubkey(Buffer param, Buffer key)

Diffie-Hellman の鍵交換のための X.509の SubjectPublicKeyInfo を取得する．

DHパラメタ(P+G)とDH鍵(Y)のDER形式を結合して,完全な鍵情報のDER形式をつくる．

@param  param  DHパラメータ(P,G鍵)．i2d_DHparams()による DER形式．
@param  key   DH公開鍵(Y鍵)の DER形式．

@return DER形式の公開鍵情報．[[Algor + [P + G + PKeySize]] + [BIT [Y]]]


参考： param のデータ形式は
@code
	[SEQ  [INT  P-key]
		  [INT  G-key]
	]
@endcode
である．これと Y-key から 下記のデータを生成する．
@code
	[SEQ  [SEQ  [OBJ  Algor]
				[SEQ  [INT  P-key]
					  [INT  G-key]
					  [INT  P-keysize - 1]
				]  
		  ]
		  [BIT(未使用bit無し)  [INT  Y-key]]
	]  
@endcode

@attention
DHのアルゴリズムは {0x06,0x07,0x2a,0x86,0x48,0xce,0x3e,0x02,0x01} だと思うのだが,RSAのアルゴリズムで動くようだ？
*/
Buffer	join_DHpubkey(Buffer param, Buffer key)
{
//	unsigned char dh_algor[]={0x06,0x07,0x2a,0x86,0x48,0xce,0x3e,0x02,0x01};			// DH  ?
	unsigned char dh_algor[]={0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x03,0x01};	// RSA ?
											// ↑ RSAアルゴリズム？ 
	int len_dh_algor = 11;

	int ls, lp, la, sz;
	Buffer  px, pp, pm;

	// [P鍵 + G鍵] → pp 
	pp = init_Buffer();
	sz = skip_DERtag(param, ASN1_SEQ, 0, &lp);
	if (sz<0) return pp;

	pp.buf	 = param.buf   + sz;
	pp.bufsz = param.bufsz - sz;
	pp.vldsz = param.vldsz - sz;

	// [鍵サイズ] → pm 
	px = int2DER(key.vldsz*8-1);
	pm = toDER(px, ASN1_INT);
	ls = pm.vldsz;
	free_Buffer(&px);

	// pp[P鍵 + G鍵] + pm[鍵サイズ] → pm 
	px = make_Buffer(lp+ls); 
	memcpy(px.buf,	pp.buf, lp);
	memcpy(px.buf+lp, pm.buf, ls);  
	px.vldsz = lp + ls;
	free_Buffer(&pm);			  // ppを freeしてはいけない 
	pm = toDER(px, ASN1_SEQ); 
	ls = pm.vldsz;
	free_Buffer(&px);

	// dh_algor[アルゴリズム] + pm[P鍵 + G鍵 + 鍵サイズ] → px 
	la = len_dh_algor; 
	pp = make_Buffer(ls+la); 
	memcpy(pp.buf,  dh_algor, la);
	memcpy(pp.buf+la, pm.buf, ls);
	pp.vldsz = ls + la;
	free_Buffer(&pm);		 
	px = toDER(pp, ASN1_SEQ);
	ls = px.vldsz;
	free_Buffer(&pp);  

	// px[アルゴリズム + P鍵 + G鍵 + 鍵サイズ] + pp[Y鍵] → pm 
	pm = toDER(key, ASN1_INT);
	pp = toDER(pm,  ASN1_BIT);
	lp = pp.vldsz;
	free_Buffer(&pm); 
	pm = make_Buffer(ls+lp);
	memcpy(pm.buf,	px.buf, ls);
	memcpy(pm.buf+ls, pp.buf, lp);
	pm.vldsz = ls + lp;
	free_Buffer(&px);
	free_Buffer(&pp);  

	pp = toDER(pm, ASN1_SEQ);
	free_Buffer(&pm);
	return pp;
}







///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ASN.1 DER
//

/**
int  skip_DERtag(Buffer param, char tag, int ls, int* lp) 

タグオブジェクト paramの中の ls の位置から[タグ(tag)]と[タグ値の長さ]を
スキップして，[タグ値]の位置(return値)とそのタグ値の長さ *lp を返す．

@param      param  タグオブジェクト全体
@param      tag    現在のタグ（確認のために指定）
@param      ls     タグオブジェクト全体内での操作開始点  
@param[out] lp     関数終了時に，そのタグのタグ値の長さが格納される．値は指定しない．

@return            そのタグのタグ値の先頭位置．

@code
参考：       ↓ls
    [タグオブジェクト] = .....[タグ(tag)][タグ値の長さ(*lp)][タグ値][タグ].......
                    ↑return
    [タグ値]として，その中に[タグオブジェクト]を含む場合もある（階層構造を取り得る）
@endcode
*/
int  skip_DERtag(Buffer param, unsigned char tag, int ls, int* lp) 
{
	int i, sz=-1;

	if ((unsigned char)param.buf[ls]==tag) {
		if ((unsigned char)param.buf[ls+1]>=0x80) {
			sz = (int)param.buf[ls+1] - 128;
			*lp = 0;
			for (i=ls+2; i<ls+sz+2; i++) {
				*lp *= 256;
				*lp += (unsigned char)param.buf[i];
			}	
		}
		else {
			sz = 0;
			*lp = (int)param.buf[ls+1];
		}
		//if ((unsigned char)param.buf[ls]==ASN1_BIT) sz++;
		sz = ls + sz + 2;
	}

	return sz;
}







/**
Buffer  toDER(Buffer pt, char tag)

データを DER形式に変換する．
現在は ASN1_INT, ASN1_SEQ, ASN1_BIT のみをサポート．

@param  pt   変換するデータ
@param  tag  データ形式のタグ．ASN1_INT, ASN1_SEQ, ASN1_BIT をサポート．デフォルト動作は  ANS1_SEQ．

@return DER形式へ変換されたデータ．
*/
Buffer  toDER(Buffer pt, char tag)
{
	int  sz, len, pp;
	unsigned char  cnt=0;
	Buffer	buf;

	len = get_DER_length(pt, tag);  
	buf = make_Buffer(len);
	sz = pt.vldsz;
	pp = len - sz;
	memcpy(buf.buf+pp, pt.buf, sz);

	pp--;
	if (tag==ASN1_INT) {
		if (pt.buf[0]>=0x80) {
			sz++;
			buf.buf[pp--] = 0x00;
		}
	}
	else if (tag==ASN1_BIT) {
		sz++;
		buf.buf[pp--] = 0x00;
	}

	buf.buf[0] = tag;
	if (sz>=128) {
		cnt++;
		while (sz>=256) {
			buf.buf[pp--] = sz % 256;
			sz >>= 8;
			cnt++;
		}
		buf.buf[pp] = (unsigned char)sz;
		buf.buf[1]  = 0x80 + cnt;
	}
	else {
		buf.buf[1] = (unsigned char)sz;
	}

	buf.vldsz = len;
	return buf;
}




/**
Buffer  int2DER(int n)

整数を DER形式のバイナリに変換する．ただし,INTタグは付けない．

@param  n  変換する整数値
@return DERのバナリ形式．

@bug この関数では,int型の範囲の数値しか扱えない．
*/
Buffer  int2DER(int n)
{
	int  ii, div, cnt;
	Buffer str;

	cnt = 1;
	div = n;
	while(div>=256) {
		div >>= 8;
		cnt++;
	}

	str = make_Buffer(cnt);
	ii = cnt - 1;

	while(n>=256) {
		str.buf[ii--] = (unsigned char)(n % 256);
		n >>= 8;
	}
	str.buf[0] = (unsigned char)n;
	str.vldsz  = cnt;

	return str;
}




/**
int  get_DER_length(Buffer pt, char tag)

データをDER形式に変換した場合の長さを計算する．
 
@param  pt   計算対象のデータ．
@param  tag  データ形式のタグ．ASN1_INT, ASN1_SEQ, ASN1_BIT をサポート．デフォルト動作は ANS1_SEQ．

@return データをDER形式に変換した場合の長さ．エラーの場合は 0．
*/
int  get_DER_length(Buffer pt, char tag)
{
	int  sz, div, cnt=0;

	sz = pt.vldsz;
	if (tag==ASN1_INT) {
		if (pt.buf[0]>=0x80) sz++;
	}
	else if (tag==ASN1_BIT) sz++;	

	div = sz;
	if (div>=128) cnt++;
	while (div>=256) {
		div >>= 8;
		cnt++;
	}
	sz = sz + cnt + 2;
	return sz;
}
	
	





///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SSL/TLS
//
// 		Reference: 	http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04s03.html
//					http://x68000.q-e-d.net/~68user/net/
//

/**
<b>SSL/TLS 関数</b>

@attention
注：この関数群では，SSL_CTX* と SSL* の変数は一対一に対応している
効率は悪いが，ソースが見易くなる．
*/



/**
void  ssl_init()

SSL/TLS の初期化を行う．これに先立って，乱数の初期化も行っておいた方が良い．

*/
void  ssl_init()
{
	SSL_library_init();
	SSL_load_error_strings();
}




/**
SSL*  ssl_client_connect(int sock)

SSL/TLSのストリームを作成し，ソケットと関連付ける．@n
これにより相手（SSLをしゃべるサーバ）とSSL/TLS通信が可能になる．

@param  sock     SSL通信を行うソケット
@param  ca       認証局の自己証明書 PEM形式
@param  mode     サーバ証明書のチェックを行うか？
@param  mode     @b ON:  行う．認証エラーの場合は NULL を返す．
@param  mode     @b OFF: ここでは行わない．ca が NULLの場合は強制的に OFFになる．

@retval NULL以外 SSL用ストリームへのポインタ．
@retval NULL     エラー．この場合は ERR_print_errors_fp(stderr); で直前のエラー内容を知ることができる．

@attention この関数によって取得した SSL* 変数は，必ず ssl_close() でクローズすること．
           SSL_shutdown(), SSL_free() を使用した場合は SSL_CTX* 変数を開放できないので，メモリリークを起こす．
*/
SSL*  ssl_client_socket(int sock, char* ca, int mode)
{
	int      ret;
	SSL*  	 ssl;
	SSL_CTX* ssl_ctx;

	//
	ssl_ctx = SSL_CTX_new(SSLv23_client_method());
	if (ssl_ctx==NULL) return NULL;

	ssl = SSL_new(ssl_ctx);
	if (ssl==NULL) {
		//DEBUG_MODE ERR_print_errors_fp(stderr);
		SSL_CTX_free(ssl_ctx);
		return NULL;
	}

	// 認証局データを読み込む（PEMデータ）
	if (ca!=NULL) SSL_CTX_load_verify_locations(ssl_ctx, ca, NULL);
	else mode = OFF;

	ret = SSL_set_fd(ssl, sock);
	if (ret!=1) {
		//DEBUG_MODE ERR_print_errors_fp(stderr);
		ssl_close(ssl);
		return NULL;
	}

	// サーバへ接続
	ret = SSL_connect(ssl);
	if (ret!=1) {
		//DEBUG_MODE ERR_print_errors_fp(stderr);
		ssl_close(ssl);
		return NULL;
	}

	// サーバ証明書のチェック
	if (mode==ON) {
		long lrt = SSL_get_verify_result(ssl);
		if (lrt!=X509_V_OK) {
			//DEBUG_MODE ERR_print_errors_fp(stderr);
			ssl_close(ssl);
			return NULL;
		}
	}

	return ssl;
}





/**
SSL*  ssl_server_socket(int sock, char* crt_fn, char* key_fn)

SSL/TLSのサーバ用TCPストリームを作成し，ソケットと関連付ける．@n
これに先立って TCPの接続を確立して置かなければならない．

@param  sock    SSL通信を行うソケット．sock は accept() の戻り値を使用する．
@param  crt_fn  サーバ証明書のファイル名（PEM形式）
@param  key_fn  サーバの秘密鍵のファイル名（PEM形式）

@return SSL用ストリームへのポインタ．エラーの場合は NULLが返る．

@attention この関数によって取得した SSL* 変数は，必ず ssl_close() でクローズすること．
           SSL_shutdown(), SSL_free() を使用した場合は SSL_CTX* 変数を開放できないので，メモリリークを起こす．

*/
SSL*  ssl_server_socket(int sock, char* crt_fn, char* key_fn)
{
	int  ret;
	SSL*  	 ssl;
	SSL_CTX* ssl_ctx;

	//
	ssl_ctx = SSL_CTX_new(SSLv23_server_method());
	if (ssl_ctx==NULL) return NULL;

	// サーバ証明書と秘密鍵の読み込み
	if (crt_fn!=NULL && key_fn!=NULL) {
		ret = SSL_CTX_use_certificate_file(ssl_ctx, crt_fn, SSL_FILETYPE_PEM);
		if (ret!=1) {
			//DEBUG_MODE ERR_print_errors_fp(stderr);
			SSL_CTX_free(ssl_ctx);
			return NULL;
		}
		ret = SSL_CTX_use_PrivateKey_file(ssl_ctx, key_fn, SSL_FILETYPE_PEM);
		if (ret!=1) {
			//DEBUG_MODE ERR_print_errors_fp(stderr);
			SSL_CTX_free(ssl_ctx);
			return NULL;
		}
		SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
		SSL_CTX_set_verify_depth(ssl_ctx, 1);
	}


	ssl = SSL_new(ssl_ctx);
	if (ssl==NULL) {
		//DEBUG_MODE ERR_print_errors_fp(stderr);
		SSL_CTX_free(ssl_ctx);
		return NULL;
	}

	ret = SSL_set_fd(ssl, sock);
	if (ret!=1) {
		//DEBUG_MODE ERR_print_errors_fp(stderr);
		ssl_close(ssl);
		return NULL;
	}

	return ssl;
}





/**
void  ssl_close(SSL* ssl)

SSLストリームをクローズし，使用した変数を開放する．

@param  ssl  クローズする SSL用ストリーム
*/
void  ssl_close(SSL* ssl)
{
	if (ssl!=NULL) {
		//SSL_CTX* p = ssl->ctx;
		SSL_shutdown(ssl);
		SSL_free(ssl);
		//if (p!=NULL) SSL_CTX_free(p);
	}
}





/**
tList*  ssl_get_cert_info(SSL* ssl)

SSLストリームから証明書の情報を得る．これに先立って 接続が connect していないといけない．

@param  ssl  SSL用ストリーム

@return SSLの情報を格納したリストへのポインタ．
        獲得できる情報は，cipher, version, subject, issuer, before, after

*/
tList*  ssl_get_cert_info(SSL* ssl)
{
	tList* lt;
	tList* lp;
	char* pp;
	char  buf[LBUF];
	X509* x509;
	BIO*  bio;


	pp = (char*)SSL_get_cipher(ssl);
	lt = lp = add_tList_node_str(NULL, "cipher", pp);

	pp = SSL_get_cipher_version(ssl);
	lp = add_tList_node_str(lp, "version", pp);


	x509 = SSL_get_peer_certificate(ssl); 
	if (x509==NULL) return lt;

	bio = BIO_new(BIO_s_mem());
	if (bio==NULL) {
		X509_free(x509);
		return lt;
	}

	X509_NAME_print_ex(bio, X509_get_subject_name(x509), 0, XN_FLAG_RFC2253);
	BIO_gets(bio, buf, LBUF);
	lp = add_tList_node_str(lp, "subject", buf);

	X509_NAME_print_ex(bio, X509_get_issuer_name(x509), 0, XN_FLAG_RFC2253);
	BIO_gets(bio, buf, LBUF);
	lp = add_tList_node_str(lp, "issuer", buf);

	ASN1_TIME_print(bio, X509_get_notBefore(x509));
	BIO_gets(bio, buf, LBUF);
	lp = add_tList_node_str(lp, "before", buf);

	ASN1_TIME_print(bio, X509_get_notAfter(x509));
	BIO_gets(bio, buf, LBUF);
	lp = add_tList_node_str(lp, "after", buf);

	BIO_free(bio);
	X509_free(x509);

	return lt;
}







/////////////////////////////////////////////////////////////////
// SSL communications

/**    
int  ssl_recv(SSL* ssl, char* rmsg, int size)
     
SSL_read()をラッピングした関数．SSL経由でデータを受信する．
     
@param  ssl     SSL用ストリーム
@param  rmsg    受信用データバッファ
@param  size    データバッファのサイズ
     
@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．

@see tcp_recv() 
*/  
int  ssl_recv(SSL* ssl, char* rmsg, int size)
{
	int cc;

	memset(rmsg, 0, size);
	cc = SSL_read(ssl, rmsg, size-1);
	 
	return cc;
}




/**    
int  ssl_send(SSL* ssl, char* smsg, int size)
     
SSL_write()をラッピングした関数．SSL経由でデータを送る．

データ(smsg)のサイズ sizeに0以下を指定した場合は、smsgは文字列であると見なして,サイズを自動的に計算する．
     
@param  ssl    SSL用ストリーム
@param  smsg   送信するデータ
@param  size   送信するデータ（smsg）のサイズ．サイズが 0以下の場合は smsgは文字列であるとみなす．
     
@retval  0以上 送信バイト数．
@retval -1     失敗した．

@see also tcp_send()
*/
int  ssl_send(SSL* ssl, char* smsg, int size)
{
	int cc;

	if (size<=0) size = strlen(smsg);
	cc = SSL_write(ssl, smsg, size);
	
	return cc;
}




/**    
int  ssl_recv_wait(int sock, SSL* ssl, char* mesg, int sz, int tm)
      
SSL経由でデータを受信する．

タイムアウトの設定が可能．タイムアウトに 0を指定した場合, recv_wait()
関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
となる (RECV_TIMEOUTEDが返る)．
   
@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  mesg    受信用データバッファ
@param  sz      データバッファのサイズ
@param  tm      タイムアウト時間．秒単位．
   
@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval RECV_TIMEOUTED  タイムアウト．

@see tcp_recv_wait() 
*/
int  ssl_recv_wait(int sock, SSL* ssl, char* mesg, int sz, int tm)
{
	int  cc;
				 
	if (recv_wait(sock, tm)) {
		cc = ssl_recv(ssl, mesg, sz);
		if (cc<=0) DEBUG_MESG("SSL_RECV_MESG: Session Closed.\n");
	}
	else {
		DEBUG_MESG("SSL_RECV_MESG: Time Out.\n");
		return RECV_TIMEOUTED;
	}
	return cc;
}




/**    
int  ssl_send_mesgln(SSL* ssl, char* mesg)
      
SSLメッセージ(文字列)に改行(@\r@\n)を付け加えて送信する．
   
@param  ssl    SSL用ストリーム
@param  mesg   送信用メッセージ
   
@retval  0以上 送信バイト数（改行を含む）．
@retval -1     失敗した(send()の戻り値)．
*/    
int  ssl_send_mesgln(SSL* ssl, char* mesg)
{  
	int	cc, sz;
	char* buf;
	
	sz = strlen(mesg)+3;	// for CR+LF+0x00 
	buf = (char*)malloc(sz);
	if (buf==NULL) return -1;
	
	strncpy(buf, mesg, sz);
	strncat(buf, "\r\n", 2);
	cc = ssl_send(ssl, buf, strlen(buf));
	
	free(buf);
	return cc;
}




/**
int  ssl_recv_mstream(int sock, SSL* ssl, char* mesg, int sz, mstream* sb, int tm)

SSL/TSL経由でメッセージ(文字列)を受信する．

受信メッセージはメッセージストリーム バッファに一旦バッファリングされ，この関数により一行ずつ読み出される．
mesgには最大 sz-1文字が格納される．もし，バッファ中の一行のデータが sz-1より大きい場合は，はみ出した部分は捨てられる．

mesgに格納される時，行中の改行コードは削除され，行末には必ず '\0' が入る．
タイムアウトの設定が可能でタイムアウトに 0を指定した場合, 呼び出した時点で
読み込み可能データがなければすぐにタイムアウトとなる (RECV_TIMEOUTED が返る)．
メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で確保される．

一旦この関数を使用して，受信データをバッファリングしたら，ソケットをクローズするまで，
読み取りには必ず同じストリームを使用してこの関数を呼び出さばけ
ればならない．そうで無い場合は受信データの整合性は保証されない．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  mesg    受信用データバッファ．予め十分なメモリ領域を確保しておく．
@param  sz      データバッファのサイズ
@param  sb      リングバッファ型のストリームバッファ．バッファ部が確保さえていなければ，自動的に確保される．
@param  tm      タイムアウト時間．秒単位．

@retval  1以上  mesgに格納したメッセージのバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval -2      引数に NULLのデータがある．
@retval -3      メッセージバッファ部が無いので確保しようとしたが，確保に失敗した．
@retval -4      メッセージバッファにデータは存在するはずだが，原因不明の理由により獲得に失敗した．
@retval -5      メッセージバッファ中のデータの長さが，mesgの長さより大きい．はみ出した部分は捨てられた．
@retval RECV_TIMEOUTED   タイムアウト．
*/
int  ssl_recv_mstream(int sock, SSL* ssl, char* mesg, int sz, mstream* sb, int tm)
{
	int  cc;
	unsigned char* pp;

	if (mesg==NULL || sb==NULL) return -2;
	memset(mesg, 0, sz);

	if (sb->buf==NULL) {
		*sb = make_mstream(RECVBUFSZ);
		if (sb->buf==NULL) return -3;
	}

	while (sb->datano==0) {
		cc = ssl_recv_wait(sock, ssl, mesg, sz, tm);
		if (cc<=0) return cc;
		put_mstream(sb, (unsigned char*)mesg);
		memset(mesg, 0, sz);
	}

	pp = get_mstream(sb);
	if (pp==NULL) return -4;
	if (strlen((const char*)pp)>=(unsigned int)sz) {
		memcpy(mesg, pp, sz-1);
		free(pp);
		return -5;
	}
	memcpy(mesg, pp, strlen((const char*)pp));

	free(pp);
	return strlen(mesg);
}






/////////////////////////////////////////////////////////////////
// SSL with Buffer

/**
int  ssl_recv_Buffer(SSL* ssl, Buffer* str)

SSL経由でデータを受信する．バッファリングなし．

@param  ssl     SSL用ストリーム
@param  str     受信用データバッファ．予めメモリを確保しておく．

@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．正常切断
@retval -1      受信エラー．
*/
int  ssl_recv_Buffer(SSL* ssl, Buffer* str)
{
	int cc;

	memset(str->buf, 0, str->bufsz);
	str->vldsz = 0;

	cc = SSL_read(ssl, str->buf, str->bufsz-1);
	if (cc>=0) str->vldsz = cc;

	return cc;
}




/**
int  ssl_send_Buffer(SSL* ssl, Buffer* str)

SSL経由でデータを送信する．

@param  ssl    SSL用ストリーム
@param  str    送信用データバッファ．

@retval  0以上 送信バイト数．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_send_Buffer(SSL* ssl, Buffer* str)
{
	int cc;

	if (str->vldsz<0) str->vldsz = strlen((const char*)str->buf);
	cc = SSL_write(ssl, str->buf, str->vldsz);
	return cc;
}





/**
int  ssl_recv_Buffer_wait(int sock, SSL* ssl, Buffer* str, int tm)

SSL経由でデータを受信する．

タイムアウトの設定が可能．タイムアウトに 0を指定した場合, recv_wait()
関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
となる (RECV_TIMEOUTEDが返る)．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  str     受信用データバッファ．予めメモリを確保しておく．
@param  tm      タイムアウト時間．秒単位．

@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval RECV_TIMEOUTED    タイムアウト．
*/
int  ssl_recv_Buffer_wait(int sock, SSL* ssl, Buffer* str, int tm)
{
	int cc;

	str->vldsz = 0;
	cc = ssl_recv_wait(sock, ssl, (char*)str->buf, str->bufsz, tm);
	if (cc>=0) str->vldsz = cc;

	return cc;
}




/**
int  ssl_send_sBuffer(SSL* ssl, Buffer* str)

SSL経由で文字列データを送信する．

@param  ssl    SSL用ストリーム
@param  str    送信用メッセージバッファ．

@retval  0以上 送信バイト数．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_send_sBuffer(SSL* ssl, Buffer* str)
{
	int cc;

	cc = SSL_write(ssl, str->buf, strlen((const char*)str->buf));
	return cc;
}





/**
int  ssl_send_sBufferln(SSL* ssl, Buffer* str)

SSLメッセージ(文字列)に改行(@\r@\n)を付け加えて送信する．

@param  ssl    SSL用ストリーム
@param  str    送信用メッセージバッファ．

@retval  0以上 送信バイト数（改行を含む）．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_send_sBufferln(SSL* ssl, Buffer* str)
{
	int	cc;
	Buffer buf;

	buf = dup_Buffer(*str);
	cat_s2Buffer("\r\n", &buf);
	cc = SSL_write(ssl, buf.buf, strlen((const char*)buf.buf));
	free_Buffer(&buf);

	return cc;
}




/**
int  ssl_recv_mstreamBuffer(int sock, SSL* ssl, Buffer* mesg, mstream* sb, int tm)

SSL経由でメッセージ(文字列)を受信する．@n
受信メッセージはメッセージストリームに一旦バッファリングされ，この関数により一行ずつ読み出される．

mesgに格納される時，行中の改行コードは削除され，行末には必ず '\0' が入る．
タイムアウトの設定が可能でタイムアウトに 0を指定した場合, 呼び出した時点で
読み込み可能データがなければすぐにタイムアウトとなる (RECV_TIMEOUTED が返る)．
メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で
確保される．

一旦この関数を使用して，受信データをバッファリングしたら，ソケットをクローズするまで，
読み取りには必ず同じストリームを使用してこの関数を呼び出さばければならない．
そうで無い場合は受信データの整合性は保証されない．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  mesg    受信用データバッファ．バッファ部を確保する必要はない．
@param  sb      メッセージバッファ（リング型のストリームバッファ）．バッファ部が確保さえていなければ，自動的に確保される．
@param  tm      タイムアウト時間．秒単位．

@retval  1以上  mesgに格納したメッセージのバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval -2      引数に NULLのデータがある．
@retval -3      メッセージバッファ部が無いので確保しようとしたが，確保に失敗した．
@retval -4      メッセージバッファにデータは存在するはずだが，原因不明の理由により獲得に失敗した．
@retval RECV_TIMEOUTED   タイムアウト．
*/
int  ssl_recv_mstreamBuffer(int sock, SSL* ssl, Buffer* mesg, mstream* sb, int tm)
{
	int	cc;
	unsigned char* pp;

	if (mesg==NULL || sb==NULL) return -2;
	*mesg = make_Buffer(LBUF);

	if (sb->buf==NULL) {
		*sb = make_mstream(RECVBUFSZ);
		if (sb->buf==NULL) return -3;
	}

	while (sb->datano==0) {
		cc = ssl_recv_Buffer_wait(sock, ssl, mesg, tm);
		if (cc<=0) {
			free_Buffer(mesg);
			return cc;
		}
		put_mstream(sb, mesg->buf);
		clear_Buffer(mesg);
	}

	pp = get_mstream(sb);
	if (pp==NULL) return -4;
	copy_s2Buffer((char*)pp, mesg);
	free(pp);

	return mesg->vldsz;
}




/**
int  ssl_recv_linesBuffer(int sock, SSL* ssl, Buffer* mesg, int tm)

SSL経由でメッセージ(文字列)を受信する．複数行分（\nで終わることが保証される）のデータを
取り出さすことのできる簡易バッファ機能付き．ここからさらに一行分のデータを取り出すには，get_line() などを使用する．

ネットワークより直接一行づつ取り出すには，tcp_recv_mstreamBuffer() を使うほうが良い．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  *mesg   受信用データバッファ．予めメモリを確保しておく．
@param  tm      タイムアウト時間．秒単位．

@retval  1以上  受信されたバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval -2      buf がNULL
@retval RECV_TIMEOUTED    タイムアウト．

@bug 受信データが必ずLFで終わることが保障されている場合にしか使用できない．つまり汎用的には「使えない」
@see tcp_recv_mstreamBuffer() 
*/
int  ssl_recv_linesBuffer(int sock, SSL* ssl, Buffer* mesg, int tm)
{
	int	cc;
	Buffer msg, buf;

	if (mesg==NULL) return -2;
	buf = make_Buffer(LBUF);

	cc = ssl_recv_Buffer_wait(sock, ssl, &buf, tm);
	while (cc>0) {
		cat_Buffer(&buf, &msg);
		if (buf.buf[cc-1]==CHAR_LF) break;
		clear_Buffer(&buf);
		cc = ssl_recv_Buffer_wait(sock, ssl, &buf, tm);
	}
	free_Buffer(&buf);
		
	if (cc<=0) {
		free_Buffer(mesg);
		return cc;
	}

	return mesg->vldsz;
}




/////////////////////////////////////////////////////////////////
// SSL abd TCP compati communications

/**    
int  ssl_tcp_recv(int sock, SSL* ssl, char* rmsg, int size)
     
sslが NULLでなければSSLで受信を行い，NULLならば通常のソケットで受信を行う．
     
@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  rmsg    受信用データバッファ
@param  size    データバッファのサイズ
     
@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval RECV_TIMEOUTED  タイムアウト．

@see tcp_recv() 
*/  
int  ssl_tcp_recv(int sock, SSL* ssl, char* rmsg, int size)
{
	int cc;

	memset(rmsg, 0, size);
	
	if (ssl!=NULL) cc = SSL_read(ssl, rmsg, size-1);
	else           cc = recv(sock, rmsg, size, 0);
	 
	return cc;
}




/**    
int  ssl_tcp_send(int sock, SSL* ssl, char* smsg, int size)
     
sslが NULLでなければSSLで送信を行い，NULLならば通常のソケットで送信を行う．

データ(smsg)のサイズ sizeに0以下を指定した場合は、smsgは文字列であると見なして,サイズを自動的に計算する．
     
@param  sock   ソケット記述子
@param  ssl    SSL用ストリーム
@param  smsg   送信するデータ
@param  size   送信するデータ（smsg）のサイズ．サイズが 0以下の場合は smsgは文字列であるとみなす．
     
@retval  0以上 送信バイト数．
@retval -1     失敗した．

@see tcp_send() 
*/
int  ssl_tcp_send(int sock, SSL* ssl, char* smsg, int size)
{
	int cc;

	if (size<=0) size = strlen(smsg);

	if (ssl!=NULL) cc = SSL_write(ssl, smsg, size);
	else           cc = send(sock, smsg, size, 0);
	
	return cc;
}




/**    
int  ssl_tcp_recv_wait(int sock, SSL* ssl, char* mesg, int sz, int tm)
      
sslが NULLでなければ，SSL経由でデータを受信する．sslがNULLなら通常のソケットで受信する．

バイナリ受信も可．タイムアウトの設定が可能．タイムアウトに 0を指定した場合, 
recv_wait()関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
となる (RECV_TIMEOUTEDが返る)．
   
@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  mesg    受信用データバッファ
@param  sz      データバッファのサイズ
@param  tm      タイムアウト時間．秒単位．
   
@retval  1以上  受信したバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval RECV_TIMEOUTED  タイムアウト．

@see tcp_recv_wait() 
*/  
int  ssl_tcp_recv_wait(int sock, SSL* ssl, char* mesg, int sz, int tm)
{
	int  cc;
				 
	if (recv_wait(sock, tm)) {
		cc = ssl_tcp_recv(sock, ssl, mesg, sz);
		if (cc<=0) DEBUG_MESG("SSL_RECV_MESG: Session Closed.\n");
	}
	else {
		DEBUG_MESG("SSL_RECV_MESG: Time Out.\n");
		return RECV_TIMEOUTED;
	}		
	return cc;
}




/**    
int  ssl_tcp_send_mesgln(int sock, SSL* ssl, char* mesg)
      
SSL or TCPメッセージ(文字列)に改行(@\r@\n)を付け加えて送信する．
   
@param  sock   ソケット記述子
@param  ssl    SSL用ストリーム
@param  mesg   送信用メッセージ
   
@retval  0以上 送信バイト数（改行を含む）．
@retval -1     失敗した(send()の戻り値)．
*/    
int  ssl_tcp_send_mesgln(int sock, SSL* ssl, char* mesg)
{  
	int	cc, sz;
	char* buf;
	
	sz = strlen(mesg)+3;	// for CR+LF+0x00 
	buf = (char*)malloc(sz);
	if (buf==NULL) return -1;
	
	strncpy(buf, mesg, sz);
	strncat(buf, "\r\n", 2);
	cc = ssl_tcp_send(sock, ssl, buf, strlen(buf));
	
	free(buf);
	return cc;
}




/**
int  ssl_tcp_recv_mstream(int sock, SSL* ssl, char* mesg, int sz, mstream* sb, int tm)

SSL/TSL/TCP経由でメッセージ(文字列)を受信する．
 
受信メッセージはメッセージストリーム バッファに一旦バッファリングされ，この関数により一行ずつ読み出される．
mesgには最大 sz-1文字が格納される．もし，バッファ中の一行のデータが sz-1より大きい場合は，
はみ出した部分は捨てられる．

mesgに格納される時，行中の改行コードは削除され，行末には必ず '\0' が入る．
タイムアウトの設定が可能でタイムアウトに 0を指定した場合, 呼び出した時点で
読み込み可能データがなければすぐにタイムアウトとなる (RECV_TIMEOUTED が返る)．
メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で確保される．

一旦この関数を使用して，受信データをバッファリングしたら，ソケットをクローズするまで，
読み取りには必ず同じストリームを使用してこの関数を呼び出さばければならない．
そうで無い場合は受信データの整合性は保証されない．

@param  sock   ソケット記述子
@param  ssl    SSL用ストリーム
@param  mesg   受信用データバッファ．予め十分なメモリ領域を確保しておく．
@param  sz     データバッファのサイズ
@param  sb     リングバッファ型のストリームバッファ．バッファ部が確保さえていなければ，自動的に確保される．
@param  tm     タイムアウト時間．秒単位．

@retval  1以上 mesgに格納したメッセージのバイト数．
@retval  0     おそらくは相手側がセッションをクローズした．
@retval -1     受信エラー．
@retval -2     引数に NULLのデータがある．
@retval -3     メッセージバッファ部が無いので確保しようとしたが，確保に失敗した．
@retval -4     メッセージバッファにデータは存在するはずだが，原因不明の理由により獲得に失敗した．
@retval -5     メッセージバッファ中のデータの長さが，mesgの長さより大きい．はみ出した部分は捨てられた．
@retval RECV_TIMEOUTED   タイムアウト．
*/
int  ssl_tcp_recv_mstream(int sock, SSL* ssl, char* mesg, int sz, mstream* sb, int tm)
{
	int  cc;
	unsigned char* pp;

	if (mesg==NULL || sb==NULL) return -2;
	memset(mesg, 0, sz);

	if (sb->buf==NULL) {
		*sb = make_mstream(RECVBUFSZ);
		if (sb->buf==NULL) return -3;
	}

	while (sb->datano==0) {
		cc = ssl_tcp_recv_wait(sock, ssl, mesg, sz, tm);
		if (cc<=0) return cc;
		put_mstream(sb, (unsigned char*)mesg);
		memset(mesg, 0, sz);
	}

	pp = get_mstream(sb);
	if (pp==NULL) return -4;
	if (strlen((const char*)pp)>=(unsigned int)sz) {
		memcpy(mesg, pp, sz-1);
		free(pp);
		return -5;
	}
	memcpy(mesg, pp, strlen((const char*)pp));

	free(pp);
	return strlen(mesg);
}






/////////////////////////////////////////////////////////////////
// SSL with Buffer

/**
int  ssl_tcp_recv_Buffer(int sock, SSL* ssl, Buffer* str)

SSL or TCP経由でデータを受信する．バッファリングなし．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  str     受信用データバッファ．予めメモリを確保しておく．

@retval  1以上  受信バイト数．
@retval  0      正常切断
@retval -1      失敗すれば-1を返す．
*/
int  ssl_tcp_recv_Buffer(int sock, SSL* ssl, Buffer* str)
{
	int cc;

	memset(str->buf, 0, str->bufsz);
	str->vldsz = 0;

	if (ssl!=NULL) cc = SSL_read(ssl, str->buf, str->bufsz-1);
	else           cc = recv(sock, str->buf, str->bufsz, 0);

	if (cc>=0) str->vldsz = cc;

	return cc;
}






/**
int  ssl_tcp_send_Buffer(int sock, SSL* ssl, Buffer* str)

SSL or TCP経由でデータを送信する．

@param  sock   ソケット記述子
@param  ssl    SSL用ストリーム
@param  str    送信用データバッファ．

@retval  0以上 送信バイト数．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_tcp_send_Buffer(int sock, SSL* ssl, Buffer* str)
{
	int cc;

	if (str->vldsz<0) str->vldsz = strlen((const char*)str->buf);

	if (ssl!=NULL) cc = SSL_write(ssl, str->buf, str->vldsz);
	else           cc = send(sock, str->buf, str->vldsz, 0);

	return cc;
}




/**
int  ssl_tcp_recv_Buffer_wait(int sock, SSL* ssl, Buffer* str, int tm)

SSL or TCP経由でデータを受信する．

タイムアウトの設定が可能．タイムアウトに 0を指定した場合, recv_wait()
関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
となる (RECV_TIMEOUTEDが返る)．

@param  sock   ソケット記述子
@param  ssl	   SSL用ストリーム
@param  str    受信用データバッファ．予めメモリを確保しておく．
@param  tm     タイムアウト時間．秒単位．

@retval  1以上 受信したバイト数．
@retval	 0	   おそらくは相手側がセッションをクローズした．
@retval -1	   受信エラー．
@retval	RECV_TIMEOUTED  タイムアウト．
*/
int  ssl_tcp_recv_Buffer_wait(int sock, SSL* ssl, Buffer* str, int tm)
{
	int cc;

	str->vldsz = 0;
	cc = ssl_tcp_recv_wait(sock, ssl, (char*)str->buf, str->bufsz, tm);
	if (cc>=0) str->vldsz = cc;

	return cc;
}





/**
int  ssl_tcp_send_sBuffer(int sock, SSL* ssl, Buffer* str)

SSL or TCP経由で文字列データを送信する．

@param  sock   ソケット記述子
@param  ssl	   SSL用ストリーム
@param  str	   送信用データバッファ．

@retval  0以上 送信バイト数．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_tcp_send_sBuffer(int sock, SSL* ssl, Buffer* str)
{
	int cc;

	if (ssl!=NULL) cc = SSL_write(ssl, str->buf, strlen((const char*)str->buf));
	else           cc = send(sock, str->buf, strlen((const char*)str->buf), 0);

	return cc;
}






/**
int  ssl_tcp_send_sBufferln(int sock, SSL* ssl, Buffer* str)

SSL or TCPメッセージ(文字列)に改行(@\r@\n)を付け加えて送信する．

@param  sock   ソケット記述子
@param  ssl	   SSL用ストリーム
@param  str    送信用メッセージバッファ．

@retval  0以上 送信バイト数(含む改行)．
@retval -1     失敗した(send()の戻り値)．
*/
int  ssl_tcp_send_sBufferln(int sock, SSL* ssl, Buffer* str)
{
	int	cc;
	Buffer buf;

	buf = dup_Buffer(*str);
	cat_s2Buffer("\r\n", &buf);

	if (ssl!=NULL) cc = SSL_write(ssl, buf.buf, strlen((const char*)buf.buf));
	else           cc = send(sock, buf.buf, strlen((const char*)buf.buf), 0);

	free_Buffer(&buf);

	return cc;
}





/**
int  ssl_tcp_recv_mstreamBuffer(int sock, SSL* ssl, Buffer* mesg, mstream* sb, int tm)

SSL or TCP経由でメッセージ(文字列)を受信する．受信メッセージはメッセージストリーム
に一旦バッファリングされ，この関数により一行ずつ読み出される．

mesgに格納される時，行中の改行コードは削除され，行末には必ず '\0' が入る．
タイムアウトの設定が可能でタイムアウトに 0を指定した場合, 呼び出した時点で
読み込み可能データがなければすぐにタイムアウトとなる (RECV_TIMEOUTED が返る)．
メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で確保される．

一旦この関数を使用して，受信データをバッファリングしたら，ソケットを
クローズするまで，読み取りには必ず同じストリームを使用してこの関数を呼び出さばけ
ればならない．そうで無い場合は受信データの整合性は保証されない．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  mesg    受信用データバッファ．バッファ部を確保する必要はない．
@param  sb      メッセージバッファ（リング型のストリームバッファ）．バッファ部が確保さえていなければ，自動的に確保される．
@param 	tm      タイムアウト時間．秒単位．

@retval  1以上	mesgに格納したメッセージのバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval -2      引数に NULLのデータがある．
@retval -3      メッセージバッファ部が無いので確保しようとしたが，確保に失敗した．
@retval -4      メッセージバッファにデータは存在するはずだが，原因不明の理由により獲得に失敗した．
@retval RECV_TIMEOUTED  タイムアウト．
*/
int  ssl_tcp_recv_mstreamBuffer(int sock, SSL* ssl, Buffer* mesg, mstream* sb, int tm)
{
	int	cc;
	unsigned char* pp;

	if (mesg==NULL || sb==NULL) return -2;
	*mesg = make_Buffer(LBUF);

	if (sb->buf==NULL) {
		*sb = make_mstream(RECVBUFSZ);
		if (sb->buf==NULL) return -3;
	}

	while (sb->datano==0) {
		cc = ssl_tcp_recv_Buffer_wait(sock, ssl, mesg, tm);
		if (cc<=0) {
			free_Buffer(mesg);
			return cc;
		}
		put_mstream(sb, mesg->buf);
		clear_Buffer(mesg);
	}

	pp = get_mstream(sb);
	if (pp==NULL) return -4;
	copy_s2Buffer((char*)pp, mesg);
	free(pp);

	return mesg->vldsz;
}




/**
int  ssl_tcp_recv_linesBuffer(int sock, SSL* ssl, Buffer* mesg, int tm)

SSL or TCP経由でメッセージ(文字列)を受信する．複数行分（\nで終わることが保証される）のデータを
取り出さすことのできる簡易バッファ機能付き．ここからさらに一行分のデータを取り出すには，
get_line() などを使用する．@n
また，ネットワークより直接一行づつ取り出すには，tcp_recv_mstreamBuffer() を使うほうが良い．

@param  sock    ソケット記述子
@param  ssl     SSL用ストリーム
@param  *mesg   受信用データバッファ．予めメモリを確保しておく．
@param  tm      タイムアウト時間．秒単位．

@retval  1以上  受信されたバイト数．
@retval  0      おそらくは相手側がセッションをクローズした．
@retval -1      受信エラー．
@retval -2      buf がNULL
@retval RECV_TIMEOUTED  タイムアウト．

@bug 受信データが必ずLFで終わることが保障されている場合にしか使用できない．つまり汎用的には「使えない」
@see tcp_recv_mstreamBuffer()

*/
int  ssl_tcp_recv_linesBuffer(int sock, SSL* ssl, Buffer* mesg, int tm)
{
	int	cc;
	Buffer msg, buf;

	if (mesg==NULL) return -3;
	buf = make_Buffer(LBUF);

	cc = ssl_tcp_recv_Buffer_wait(sock, ssl, &buf, tm);
	while (cc>0) {
		cat_Buffer(&buf, &msg);
		if (buf.buf[cc-1]==CHAR_LF) break;
		clear_Buffer(&buf);
		cc = ssl_tcp_recv_Buffer_wait(sock, ssl, &buf, tm);
	}
	free_Buffer(&buf);
		
	if (cc<=0) {
		free_Buffer(mesg);
		return cc;
	}

	return mesg->vldsz;
}





#endif
