text:  
  /**********************************************************************
   *                          gost_keyx.c                               *
   *             Copyright (c) 2005-2006 Cryptocom LTD                  *
   *         This file is distributed under the same license as OpenSSL *
   *                                                                    *
   *   VK0 34.10-2001 key exchange and GOST R 34.10-2001                *
   *   based PKCS7/SMIME support                                        *
   *          Requires OpenSSL 0.9.9 for compilation                    *
   **********************************************************************/
  #include <openssl/evp.h>
  #include <openssl/rand.h>
  #include <string.h>
  #include <openssl/objects.h>
  #include "gost89.h"
  #include "gosthash.h"
  #include "e_gost_err.h"
  #include "gost_keywrap.h"
  #include "gost_lcl.h"
  #include "gost2001_keyx.h"
  
  
  
  /* Implementation of CryptoPro VKO 34.10-2001 algorithm */
  static int VKO_compute_key(unsigned char *shared_key,size_t shared_key_size,const EC_POINT *pub_key,EC_KEY *priv_key,const unsigned char *ukm)
  	{
  	unsigned char ukm_be[8],databuf[64],hashbuf[64];
  	BIGNUM *UKM=NULL,*p=NULL,*order=NULL,*X=NULL,*Y=NULL;
  	const BIGNUM* key=EC_KEY_get0_private_key(priv_key);
  	EC_POINT *pnt=EC_POINT_new(EC_KEY_get0_group(priv_key));
  	int i;
  	gost_hash_ctx hash_ctx;
  	BN_CTX *ctx = BN_CTX_new();
  
  	for (i=0;i<8;i++)
  		{
  		ukm_be[7-i]=ukm[i];
  		}
  	BN_CTX_start(ctx);
  	UKM=getbnfrombuf(ukm_be,8);
  	p=BN_CTX_get(ctx);
  	order = BN_CTX_get(ctx);
  	X=BN_CTX_get(ctx);
  	Y=BN_CTX_get(ctx);
  	EC_GROUP_get_order(EC_KEY_get0_group(priv_key),order,ctx);
  	BN_mod_mul(p,key,UKM,order,ctx);	
  	EC_POINT_mul(EC_KEY_get0_group(priv_key),pnt,NULL,pub_key,p,ctx);
  	EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
  		pnt,X,Y,ctx);
  	/*Serialize elliptic curve point same way as we do it when saving
  	 * key */
  	store_bignum(Y,databuf,32);
  	store_bignum(X,databuf+32,32);
   	/* And reverse byte order of whole buffer */
  	for (i=0;i<64;i++)
  		{
  		hashbuf[63-i]=databuf[i];
  		}
  	init_gost_hash_ctx(&hash_ctx,&GostR3411_94_CryptoProParamSet);
  	start_hash(&hash_ctx);
  	hash_block(&hash_ctx,hashbuf,64);
  	finish_hash(&hash_ctx,shared_key);
  	done_gost_hash_ctx(&hash_ctx);
  	BN_free(UKM);
  	BN_CTX_end(ctx);
  	BN_CTX_free(ctx);
  	EC_POINT_free(pnt);
  	return 32;
  	}
  
  
  /*
   * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
   * algorithm
   */
  int pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
  {
  	/* Public key of peer in the ctx field peerkey
  	 * Our private key in the ctx pkey
  	 * ukm is in the algorithm specific context data
  	 */
  	EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
  	EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
  	struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
  	
  	if (!data->shared_ukm) {
  		GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
  		return 0;
  	}	
  
  	if (key == NULL) {
  		*keylen = 32;
  		return 32;
  	}	
  	
  	*keylen=VKO_compute_key(key, 32, EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
  		(EC_KEY *)EVP_PKEY_get0(my_key),data->shared_ukm);
  	return 1;	
  }
  
  
  
  
  /*  
   * EVP_PKEY_METHOD callback encrypt  
   * Implementation of GOST2001 key transport, cryptocom variation 
   */
  /* Generates ephemeral key based on pubk algorithm
   * computes shared key using VKO and returns filled up
   * GOST_KEY_TRANSPORT structure
   */
  
  /*  
   * EVP_PKEY_METHOD callback encrypt  
   * Implementation of GOST2001 key transport, cryptopo variation 
   */
  
  int pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key,size_t key_len) 
  	{
  	GOST_KEY_TRANSPORT *gkt=NULL; 
  	EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
  	struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
  	const struct gost_cipher_info *param=get_encryption_params(NULL);
  	unsigned char ukm[8], shared_key[32], crypted_key[44];
  	int ret=0;
  	int key_is_ephemeral=1;
  	gost_ctx cctx;
  	EVP_PKEY *sec_key=EVP_PKEY_CTX_get0_peerkey(pctx);
  	if (data->shared_ukm) 
  		{
  		memcpy(ukm, data->shared_ukm,8);
  		} 
  	else if (out) 
  		{
  		
  		if (RAND_bytes(ukm,8)<=0)
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
  				GOST_R_RANDOM_GENERATOR_FAILURE);
  			return 0;
  			}	
  		}	
  	/* Check for private key in the peer_key of context */	
  	if (sec_key) 
  		{
  		key_is_ephemeral=0;
  		if (!gost_get0_priv_key(sec_key)) 
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
  			GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
  			goto err;
  			}	
  		} 
  	else 
  		{
  		key_is_ephemeral=1;
  		if (out) 
  			{
  			sec_key = EVP_PKEY_new();
  			EVP_PKEY_assign(sec_key,EVP_PKEY_base_id(pubk),EC_KEY_new());
  			EVP_PKEY_copy_parameters(sec_key,pubk);
  			if (!gost2001_keygen(EVP_PKEY_get0(sec_key))) 
  				{
  				goto err;
  				}	
  			}
  		}
  	if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS) && param ==  gost_cipher_list)
  		{
  		param= gost_cipher_list+1;
  		}	
      if (out) 
  		{
  		VKO_compute_key(shared_key,32,EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),EVP_PKEY_get0(sec_key),ukm);
  		gost_init(&cctx,param->sblock);	
  		keyWrapCryptoPro(&cctx,shared_key,ukm,key,crypted_key);
  		}
  	gkt = GOST_KEY_TRANSPORT_new();
  	if (!gkt)
  		{
  		goto err;
  		}	
  	if(!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,
  			ukm,8))
  		{
  		goto err;
  		}	
  	if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,crypted_key+40,4))
  		{
  		goto err;
  		}
  	if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,crypted_key+8,32))
  		{
  		goto err;
  		}
  	if (key_is_ephemeral) {	
  		if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,out?sec_key:pubk))
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
  					GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
  			goto err;
  			}	
  	}		
  	ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
  	gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
  	if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
  	if (!key_is_ephemeral)
  		{
  		/* Set control "public key from client certificate used" */
  		if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
  				GOST_R_CTRL_CALL_FAILED);
  			goto err;
  			}
  		}
  	if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,out?&out:NULL))>0) ret =1;
  	GOST_KEY_TRANSPORT_free(gkt);
  	return ret;	
  	err:		
  	if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
  	GOST_KEY_TRANSPORT_free(gkt);
  	return -1;
  	}
  /*  
   * EVP_PKEY_METHOD callback decrypt  
   * Implementation of GOST2001 key transport, cryptopo variation 
   */
  int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_len, const unsigned char *in, size_t in_len)
  	{
  	const unsigned char *p = in;
  	EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
  	GOST_KEY_TRANSPORT *gkt = NULL;
  	int ret=0;	
  	unsigned char wrappedKey[44];
  	unsigned char sharedKey[32];
  	gost_ctx ctx;
  	const struct gost_cipher_info *param=NULL;
  	EVP_PKEY *eph_key=NULL, *peerkey=NULL;
  
  	if (!key)
  		{
  		*key_len = 32;
  		return 1;
  		}	
  	gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
  		in_len);
  	if (!gkt)
  		{
  		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
  		return -1;
  		}	
  
  	/* If key transport structure contains public key, use it */
  	eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
  	if (eph_key)
  		{
  		if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0)
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
  				GOST_R_INCOMPATIBLE_PEER_KEY);
  			goto err;
  			}
  		}
  	else
  		{
  		/* Set control "public key from client certificate used" */
  		if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
  			{
  			GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
  				GOST_R_CTRL_CALL_FAILED);
  			goto err;
  			}
  		}
  	peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
  	if (!peerkey)
  		{
  		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
  			GOST_R_NO_PEER_KEY);
  		goto err;
  		}
  		
  	param = get_encryption_params(gkt->key_agreement_info->cipher);
      if(!param){
          goto err;
      }
  
  	gost_init(&ctx,param->sblock);	
  	OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
  	memcpy(wrappedKey,gkt->key_agreement_info->eph_iv->data,8);
  	OPENSSL_assert(gkt->key_info->encrypted_key->length==32);
  	memcpy(wrappedKey+8,gkt->key_info->encrypted_key->data,32);
  	OPENSSL_assert(gkt->key_info->imit->length==4);
  	memcpy(wrappedKey+40,gkt->key_info->imit->data,4);	
  	VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
  		EVP_PKEY_get0(priv),wrappedKey);
  	if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key))
  		{
  		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
  			GOST_R_ERROR_COMPUTING_SHARED_KEY);
  		goto err;
  		}	
  				
  	ret=1;
  err:	
  	if (eph_key) EVP_PKEY_free(eph_key);
  	if (gkt) GOST_KEY_TRANSPORT_free(gkt);
  	return ret;
  	}
  