1238384Sjkim/**********************************************************************
2238384Sjkim *                          gost_keyx.c                               *
3238384Sjkim *             Copyright (c) 2005-2006 Cryptocom LTD                  *
4238384Sjkim *         This file is distributed under the same license as OpenSSL *
5238384Sjkim *                                                                    *
6238384Sjkim *   VK0 34.10-2001 key exchange and GOST R 34.10-2001                *
7238384Sjkim *   based PKCS7/SMIME support                                        *
8238384Sjkim *          Requires OpenSSL 0.9.9 for compilation                    *
9238384Sjkim **********************************************************************/
10238384Sjkim#include <openssl/evp.h>
11238384Sjkim#include <openssl/rand.h>
12238384Sjkim#include <string.h>
13238384Sjkim#include <openssl/objects.h>
14238384Sjkim#include "gost89.h"
15238384Sjkim#include "gosthash.h"
16238384Sjkim#include "e_gost_err.h"
17238384Sjkim#include "gost_keywrap.h"
18238384Sjkim#include "gost_lcl.h"
19238384Sjkim#include "gost2001_keyx.h"
20238384Sjkim
21238384Sjkim
22238384Sjkim
23238384Sjkim/* Implementation of CryptoPro VKO 34.10-2001 algorithm */
24238384Sjkimstatic 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)
25238384Sjkim	{
26238384Sjkim	unsigned char ukm_be[8],databuf[64],hashbuf[64];
27238384Sjkim	BIGNUM *UKM=NULL,*p=NULL,*order=NULL,*X=NULL,*Y=NULL;
28238384Sjkim	const BIGNUM* key=EC_KEY_get0_private_key(priv_key);
29238384Sjkim	EC_POINT *pnt=EC_POINT_new(EC_KEY_get0_group(priv_key));
30238384Sjkim	int i;
31238384Sjkim	gost_hash_ctx hash_ctx;
32238384Sjkim	BN_CTX *ctx = BN_CTX_new();
33238384Sjkim
34238384Sjkim	for (i=0;i<8;i++)
35238384Sjkim		{
36238384Sjkim		ukm_be[7-i]=ukm[i];
37238384Sjkim		}
38238384Sjkim	BN_CTX_start(ctx);
39238384Sjkim	UKM=getbnfrombuf(ukm_be,8);
40238384Sjkim	p=BN_CTX_get(ctx);
41238384Sjkim	order = BN_CTX_get(ctx);
42238384Sjkim	X=BN_CTX_get(ctx);
43238384Sjkim	Y=BN_CTX_get(ctx);
44238384Sjkim	EC_GROUP_get_order(EC_KEY_get0_group(priv_key),order,ctx);
45238384Sjkim	BN_mod_mul(p,key,UKM,order,ctx);
46238384Sjkim	EC_POINT_mul(EC_KEY_get0_group(priv_key),pnt,NULL,pub_key,p,ctx);
47238384Sjkim	EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
48238384Sjkim		pnt,X,Y,ctx);
49238384Sjkim	/*Serialize elliptic curve point same way as we do it when saving
50238384Sjkim	 * key */
51238384Sjkim	store_bignum(Y,databuf,32);
52238384Sjkim	store_bignum(X,databuf+32,32);
53238384Sjkim 	/* And reverse byte order of whole buffer */
54238384Sjkim	for (i=0;i<64;i++)
55238384Sjkim		{
56238384Sjkim		hashbuf[63-i]=databuf[i];
57238384Sjkim		}
58238384Sjkim	init_gost_hash_ctx(&hash_ctx,&GostR3411_94_CryptoProParamSet);
59238384Sjkim	start_hash(&hash_ctx);
60238384Sjkim	hash_block(&hash_ctx,hashbuf,64);
61238384Sjkim	finish_hash(&hash_ctx,shared_key);
62238384Sjkim	done_gost_hash_ctx(&hash_ctx);
63238384Sjkim	BN_free(UKM);
64238384Sjkim	BN_CTX_end(ctx);
65238384Sjkim	BN_CTX_free(ctx);
66238384Sjkim	EC_POINT_free(pnt);
67238384Sjkim	return 32;
68238384Sjkim	}
69238384Sjkim
70238384Sjkim
71238384Sjkim/*
72238384Sjkim * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
73238384Sjkim * algorithm
74238384Sjkim */
75238384Sjkimint pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
76238384Sjkim{
77238384Sjkim	/* Public key of peer in the ctx field peerkey
78238384Sjkim	 * Our private key in the ctx pkey
79238384Sjkim	 * ukm is in the algorithm specific context data
80238384Sjkim	 */
81238384Sjkim	EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
82238384Sjkim	EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
83238384Sjkim	struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
84238384Sjkim
85238384Sjkim	if (!data->shared_ukm) {
86238384Sjkim		GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
87238384Sjkim		return 0;
88238384Sjkim	}
89238384Sjkim
90238384Sjkim	if (key == NULL) {
91238384Sjkim		*keylen = 32;
92238384Sjkim		return 32;
93238384Sjkim	}
94238384Sjkim
95238384Sjkim	*keylen=VKO_compute_key(key, 32, EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
96238384Sjkim		(EC_KEY *)EVP_PKEY_get0(my_key),data->shared_ukm);
97238384Sjkim	return 1;
98238384Sjkim}
99238384Sjkim
100238384Sjkim
101238384Sjkim
102238384Sjkim
103238384Sjkim/*
104238384Sjkim * EVP_PKEY_METHOD callback encrypt
105238384Sjkim * Implementation of GOST2001 key transport, cryptocom variation
106238384Sjkim */
107238384Sjkim/* Generates ephemeral key based on pubk algorithm
108238384Sjkim * computes shared key using VKO and returns filled up
109238384Sjkim * GOST_KEY_TRANSPORT structure
110238384Sjkim */
111238384Sjkim
112238384Sjkim/*
113238384Sjkim * EVP_PKEY_METHOD callback encrypt
114238384Sjkim * Implementation of GOST2001 key transport, cryptopo variation
115238384Sjkim */
116238384Sjkim
117238384Sjkimint pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key,size_t key_len)
118238384Sjkim	{
119238384Sjkim	GOST_KEY_TRANSPORT *gkt=NULL;
120238384Sjkim	EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
121238384Sjkim	struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
122238384Sjkim	const struct gost_cipher_info *param=get_encryption_params(NULL);
123238384Sjkim	unsigned char ukm[8], shared_key[32], crypted_key[44];
124238384Sjkim	int ret=0;
125238384Sjkim	int key_is_ephemeral=1;
126238384Sjkim	gost_ctx cctx;
127238384Sjkim	EVP_PKEY *sec_key=EVP_PKEY_CTX_get0_peerkey(pctx);
128238384Sjkim	if (data->shared_ukm)
129238384Sjkim		{
130238384Sjkim		memcpy(ukm, data->shared_ukm,8);
131238384Sjkim		}
132238384Sjkim	else if (out)
133238384Sjkim		{
134238384Sjkim
135238384Sjkim		if (RAND_bytes(ukm,8)<=0)
136238384Sjkim			{
137238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
138238384Sjkim				GOST_R_RANDOM_GENERATOR_FAILURE);
139238384Sjkim			return 0;
140238384Sjkim			}
141238384Sjkim		}
142238384Sjkim	/* Check for private key in the peer_key of context */
143238384Sjkim	if (sec_key)
144238384Sjkim		{
145238384Sjkim		key_is_ephemeral=0;
146238384Sjkim		if (!gost_get0_priv_key(sec_key))
147238384Sjkim			{
148238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
149238384Sjkim			GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
150238384Sjkim			goto err;
151238384Sjkim			}
152238384Sjkim		}
153238384Sjkim	else
154238384Sjkim		{
155238384Sjkim		key_is_ephemeral=1;
156238384Sjkim		if (out)
157238384Sjkim			{
158238384Sjkim			sec_key = EVP_PKEY_new();
159238384Sjkim			EVP_PKEY_assign(sec_key,EVP_PKEY_base_id(pubk),EC_KEY_new());
160238384Sjkim			EVP_PKEY_copy_parameters(sec_key,pubk);
161238384Sjkim			if (!gost2001_keygen(EVP_PKEY_get0(sec_key)))
162238384Sjkim				{
163238384Sjkim				goto err;
164238384Sjkim				}
165238384Sjkim			}
166238384Sjkim		}
167238384Sjkim	if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS) && param ==  gost_cipher_list)
168238384Sjkim		{
169238384Sjkim		param= gost_cipher_list+1;
170238384Sjkim		}
171238384Sjkim    if (out)
172238384Sjkim		{
173238384Sjkim		VKO_compute_key(shared_key,32,EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),EVP_PKEY_get0(sec_key),ukm);
174238384Sjkim		gost_init(&cctx,param->sblock);
175238384Sjkim		keyWrapCryptoPro(&cctx,shared_key,ukm,key,crypted_key);
176238384Sjkim		}
177238384Sjkim	gkt = GOST_KEY_TRANSPORT_new();
178238384Sjkim	if (!gkt)
179238384Sjkim		{
180238384Sjkim		goto err;
181238384Sjkim		}
182238384Sjkim	if(!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,
183238384Sjkim			ukm,8))
184238384Sjkim		{
185238384Sjkim		goto err;
186238384Sjkim		}
187238384Sjkim	if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,crypted_key+40,4))
188238384Sjkim		{
189238384Sjkim		goto err;
190238384Sjkim		}
191238384Sjkim	if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,crypted_key+8,32))
192238384Sjkim		{
193238384Sjkim		goto err;
194238384Sjkim		}
195238384Sjkim	if (key_is_ephemeral) {
196238384Sjkim		if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,out?sec_key:pubk))
197238384Sjkim			{
198238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
199238384Sjkim					GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
200238384Sjkim			goto err;
201238384Sjkim			}
202238384Sjkim	}
203238384Sjkim	ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
204238384Sjkim	gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
205238384Sjkim	if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
206238384Sjkim	if (!key_is_ephemeral)
207238384Sjkim		{
208238384Sjkim		/* Set control "public key from client certificate used" */
209238384Sjkim		if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
210238384Sjkim			{
211238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
212238384Sjkim				GOST_R_CTRL_CALL_FAILED);
213238384Sjkim			goto err;
214238384Sjkim			}
215238384Sjkim		}
216238384Sjkim	if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,out?&out:NULL))>0) ret =1;
217238384Sjkim	GOST_KEY_TRANSPORT_free(gkt);
218238384Sjkim	return ret;
219238384Sjkim	err:
220238384Sjkim	if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
221238384Sjkim	GOST_KEY_TRANSPORT_free(gkt);
222238384Sjkim	return -1;
223238384Sjkim	}
224238384Sjkim/*
225238384Sjkim * EVP_PKEY_METHOD callback decrypt
226238384Sjkim * Implementation of GOST2001 key transport, cryptopo variation
227238384Sjkim */
228238384Sjkimint pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_len, const unsigned char *in, size_t in_len)
229238384Sjkim	{
230238384Sjkim	const unsigned char *p = in;
231238384Sjkim	EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
232238384Sjkim	GOST_KEY_TRANSPORT *gkt = NULL;
233238384Sjkim	int ret=0;
234238384Sjkim	unsigned char wrappedKey[44];
235238384Sjkim	unsigned char sharedKey[32];
236238384Sjkim	gost_ctx ctx;
237238384Sjkim	const struct gost_cipher_info *param=NULL;
238238384Sjkim	EVP_PKEY *eph_key=NULL, *peerkey=NULL;
239238384Sjkim
240238384Sjkim	if (!key)
241238384Sjkim		{
242238384Sjkim		*key_len = 32;
243238384Sjkim		return 1;
244238384Sjkim		}
245238384Sjkim	gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
246238384Sjkim		in_len);
247238384Sjkim	if (!gkt)
248238384Sjkim		{
249238384Sjkim		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
250238384Sjkim		return -1;
251238384Sjkim		}
252238384Sjkim
253238384Sjkim	/* If key transport structure contains public key, use it */
254238384Sjkim	eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
255238384Sjkim	if (eph_key)
256238384Sjkim		{
257238384Sjkim		if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0)
258238384Sjkim			{
259238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
260238384Sjkim				GOST_R_INCOMPATIBLE_PEER_KEY);
261238384Sjkim			goto err;
262238384Sjkim			}
263238384Sjkim		}
264238384Sjkim	else
265238384Sjkim		{
266238384Sjkim		/* Set control "public key from client certificate used" */
267238384Sjkim		if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
268238384Sjkim			{
269238384Sjkim			GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
270238384Sjkim				GOST_R_CTRL_CALL_FAILED);
271238384Sjkim			goto err;
272238384Sjkim			}
273238384Sjkim		}
274238384Sjkim	peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
275238384Sjkim	if (!peerkey)
276238384Sjkim		{
277238384Sjkim		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
278238384Sjkim			GOST_R_NO_PEER_KEY);
279238384Sjkim		goto err;
280238384Sjkim		}
281238384Sjkim
282238384Sjkim	param = get_encryption_params(gkt->key_agreement_info->cipher);
283238384Sjkim    if(!param){
284238384Sjkim        goto err;
285238384Sjkim    }
286238384Sjkim
287238384Sjkim	gost_init(&ctx,param->sblock);
288238384Sjkim	OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
289238384Sjkim	memcpy(wrappedKey,gkt->key_agreement_info->eph_iv->data,8);
290238384Sjkim	OPENSSL_assert(gkt->key_info->encrypted_key->length==32);
291238384Sjkim	memcpy(wrappedKey+8,gkt->key_info->encrypted_key->data,32);
292238384Sjkim	OPENSSL_assert(gkt->key_info->imit->length==4);
293238384Sjkim	memcpy(wrappedKey+40,gkt->key_info->imit->data,4);
294238384Sjkim	VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
295238384Sjkim		EVP_PKEY_get0(priv),wrappedKey);
296238384Sjkim	if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key))
297238384Sjkim		{
298238384Sjkim		GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
299238384Sjkim			GOST_R_ERROR_COMPUTING_SHARED_KEY);
300238384Sjkim		goto err;
301238384Sjkim		}
302238384Sjkim
303238384Sjkim	ret=1;
304238384Sjkimerr:
305238384Sjkim	if (eph_key) EVP_PKEY_free(eph_key);
306238384Sjkim	if (gkt) GOST_KEY_TRANSPORT_free(gkt);
307238384Sjkim	return ret;
308238384Sjkim	}
309