Updated version of gost engine.
[openssl.git] / engines / ccgost / gost2001_keyx.c
1 /**********************************************************************
2  *                          gost_keyx.c                               *
3  *             Copyright (c) 2005-2006 Cryptocom LTD                  *
4  *         This file is distributed under the same license as OpenSSL *
5  *                                                                    *
6  *   VK0 34.10-2001 key exchange and GOST R 34.10-2001                *
7  *   based PKCS7/SMIME support                                        *
8  *          Requires OpenSSL 0.9.9 for compilation                    *
9  **********************************************************************/
10 #include <openssl/evp.h>
11 #include <openssl/rand.h>
12 #include <string.h>
13 #include <openssl/objects.h>
14 #include "gost89.h"
15 #include "gosthash.h"
16 #include "e_gost_err.h"
17 #include "gost_keywrap.h"
18 #include "gost_lcl.h"
19
20 /* Transform ECDH shared key into little endian as required by Cryptocom
21  * key exchange */
22 static void *make_key_le(const void *in, size_t inlen, void *out, size_t *outlen)
23         {
24         const char* inbuf= in;
25         char* outbuf= out;
26         int i;
27         if (*outlen < inlen)
28                 {
29                 return NULL;
30                 }
31         for (i=0;i<inlen;i++)
32                 {
33                 outbuf[inlen-1-i]=inbuf[i];
34                 }
35         *outlen = inlen;
36         return out;
37         }       
38
39 /* Create gost 2001 ephemeral key with same parameters as peer key */
40 static EC_KEY *make_ec_ephemeral_key(EC_KEY *peer_key,BIGNUM *seckey)
41         {
42         EC_KEY *out = EC_KEY_new();
43         EC_KEY_copy(out,peer_key);
44         EC_KEY_set_private_key(out,seckey);
45         gost2001_compute_public(out);
46         return out;
47         }
48 /* Packs GOST elliptic curve key into EVP_PKEY setting same parameters
49  * as in passed pubkey
50  */
51 static EVP_PKEY *ec_ephemeral_key_to_EVP(EVP_PKEY *pubk,int type,EC_KEY *ephemeral) 
52         {
53         EVP_PKEY *newkey;
54         newkey = EVP_PKEY_new();
55         EVP_PKEY_assign(newkey,type,ephemeral);
56         return newkey;
57         }       
58
59 /*  
60  * EVP_PKEY_METHOD callback encrypt  
61  * Implementation of GOST2001 key transport, cryptocom variation 
62  */
63
64 int pkey_GOST01cc_encrypt (EVP_PKEY_CTX *pctx,unsigned char *out, 
65         size_t *out_len, const unsigned char *key,size_t key_len)
66         {
67         EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
68         struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);     
69         GOST_KEY_TRANSPORT *gkt = NULL;
70         int ret=0;
71         gost_ctx ctx;
72         EC_KEY *ephemeral=NULL;
73         const EC_POINT *pub_key_point=NULL;
74         unsigned char shared_key[32],encrypted_key[32],hmac[4],
75                 iv[8]={0,0,0,0,0,0,0,0};
76         ephemeral = make_ec_ephemeral_key(EVP_PKEY_get0(pubk), gost_get_priv_key(data->eph_seckey));
77         if (!ephemeral) goto err;
78         /* compute shared key */
79         pub_key_point=EC_KEY_get0_public_key(EVP_PKEY_get0(pubk));
80         if (!ECDH_compute_key(shared_key,32,pub_key_point,ephemeral,make_key_le)) 
81                 {
82                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_COMPUTING_SHARED_KEY);
83                 goto err;
84                 }       
85         /* encrypt session key */
86         gost_init(&ctx, &GostR3411_94_CryptoProParamSet);
87         gost_key(&ctx,shared_key);
88         encrypt_cryptocom_key(key,key_len,encrypted_key,&ctx);
89         /* compute hmac of session key */
90         if (!gost_mac(&ctx,32,key,32,hmac)) 
91                 {
92                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_COMPUTING_MAC);
93                 return -1;
94                 }
95         gkt = GOST_KEY_TRANSPORT_new();
96         if (!gkt) 
97                 {
98                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_NO_MEMORY);
99                 return -1;
100                 }       
101         /* Store IV which is always zero in our case */
102         if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,iv,8))
103                 {
104                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_IV);
105                 goto err;
106                 }       
107         if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,hmac,4)) 
108                 {
109                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_MAC);
110                 goto err;
111                 }       
112         if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,encrypted_key,32))
113                 {       
114                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_ERROR_STORING_ENCRYPTED_KEY);
115                 goto err;
116                 }
117         
118         if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,data->eph_seckey))
119                 {
120                 GOSTerr(GOST_F_PKEY_GOST01CC_ENCRYPT,GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
121                 goto err;
122                 }       
123         ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
124         gkt->key_agreement_info->cipher = OBJ_nid2obj(NID_id_Gost28147_89_cc);
125         if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,&out))>0) ret = 1;
126         ;
127         err:
128         if (gkt) GOST_KEY_TRANSPORT_free(gkt);
129         return ret;
130         }
131 /*  
132  * EVP_PKEY_METHOD callback decrypt  
133  * Implementation of GOST2001 key transport, cryptocom variation 
134  */
135 int pkey_GOST01cc_decrypt (EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len, const unsigned char *in, size_t in_len)
136         {
137         /* Form DH params from compute shared key */
138         EVP_PKEY *priv=EVP_PKEY_CTX_get0_pkey(pctx);
139         GOST_KEY_TRANSPORT *gkt = NULL;
140         const unsigned char *p=in;
141         unsigned char shared_key[32];
142         unsigned char hmac[4],hmac_comp[4];
143         unsigned char iv[8];
144         int i;
145         gost_ctx ctx;
146         const EC_POINT *pub_key_point;
147         EVP_PKEY *eph_key;
148
149         if (!key)
150                 {
151                 *key_len = 32;
152                 return 1;
153                 }       
154         /* Parse passed octet string and find out public key, iv and HMAC*/
155         gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
156                 in_len);
157         if (!gkt)
158                 {
159                 GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
160                 return 0;
161                 }       
162         eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
163         /* Initialization vector is really ignored here */
164         OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
165         memcpy(iv,gkt->key_agreement_info->eph_iv->data,8);
166         /* HMAC should be computed and checked */
167         OPENSSL_assert(gkt->key_info->imit->length==4);
168         memcpy(hmac,gkt->key_info->imit->data,4);       
169         /* Compute shared key */
170         pub_key_point=EC_KEY_get0_public_key(EVP_PKEY_get0(eph_key));
171         i=ECDH_compute_key(shared_key,32,pub_key_point,EVP_PKEY_get0(priv),make_key_le);
172         EVP_PKEY_free(eph_key);
173         if (!i) 
174                 {
175                 GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_COMPUTING_SHARED_KEY);
176                 GOST_KEY_TRANSPORT_free(gkt);
177                 return 0;
178                 }
179         /* Decrypt session key */
180         gost_init(&ctx, &GostR3411_94_CryptoProParamSet);
181         gost_key(&ctx,shared_key);
182         
183         if (!decrypt_cryptocom_key(key,*key_len,gkt->key_info->encrypted_key->data, 
184                         gkt->key_info->encrypted_key->length, &ctx)) 
185                 {
186                 GOST_KEY_TRANSPORT_free(gkt);
187                 return 0;
188                 }
189         GOST_KEY_TRANSPORT_free(gkt);
190         /* check HMAC of session key*/
191         if (!gost_mac(&ctx,32,key,32,hmac_comp))
192                 {
193                 GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_ERROR_COMPUTING_MAC);
194                 return 0;
195                 }
196         /* HMAC of session key is not correct */
197     if (memcmp(hmac,hmac_comp,4)!=0)
198                 {
199                 GOSTerr(GOST_F_PKEY_GOST01CC_DECRYPT,GOST_R_SESSION_KEY_MAC_DOES_NOT_MATCH);
200                 return 0;
201                 }       
202         return 1; 
203         }
204
205 /* Implementation of CryptoPro VKO 34.10-2001 algorithm */
206 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)
207         {
208         unsigned char ukm_be[8],databuf[64],hashbuf[64];
209         BIGNUM *UKM=NULL,*p=NULL,*order=NULL,*X=NULL,*Y=NULL;
210         const BIGNUM* key=EC_KEY_get0_private_key(priv_key);
211         EC_POINT *pnt=EC_POINT_new(EC_KEY_get0_group(priv_key));
212         int i;
213         gost_hash_ctx hash_ctx;
214         BN_CTX *ctx = BN_CTX_new();
215
216         for (i=0;i<8;i++)
217                 {
218                 ukm_be[7-i]=ukm[i];
219                 }
220         BN_CTX_start(ctx);
221         UKM=getbnfrombuf(ukm_be,8);
222         p=BN_CTX_get(ctx);
223         order = BN_CTX_get(ctx);
224         X=BN_CTX_get(ctx);
225         Y=BN_CTX_get(ctx);
226         EC_GROUP_get_order(EC_KEY_get0_group(priv_key),order,ctx);
227         BN_mod_mul(p,key,UKM,order,ctx);        
228         EC_POINT_mul(EC_KEY_get0_group(priv_key),pnt,NULL,pub_key,p,ctx);
229         EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
230                 pnt,X,Y,ctx);
231         /*Serialize elliptic curve point same way as we do it when saving
232          * key */
233         store_bignum(Y,databuf,32);
234         store_bignum(X,databuf+32,32);
235         /* And reverse byte order of whole buffer */
236         for (i=0;i<64;i++)
237                 {
238                 hashbuf[63-i]=databuf[i];
239                 }
240         init_gost_hash_ctx(&hash_ctx,&GostR3411_94_CryptoProParamSet);
241         start_hash(&hash_ctx);
242         hash_block(&hash_ctx,hashbuf,64);
243         finish_hash(&hash_ctx,shared_key);
244         done_gost_hash_ctx(&hash_ctx);
245         BN_free(UKM);
246         BN_CTX_end(ctx);
247         BN_CTX_free(ctx);
248         EC_POINT_free(pnt);
249         return 32;
250         }
251
252 /* Generates ephemeral key based on pubk algorithm
253  * computes shared key using VKO and returns filled up
254  * GOST_KEY_TRANSPORT structure
255  */
256 /* Public, because it would be needed in SSL implementation */
257 GOST_KEY_TRANSPORT *make_rfc4490_keytransport_2001(EVP_PKEY *pubk,BIGNUM *eph_key,
258         const unsigned char *key,size_t keylen, unsigned char *ukm,
259         size_t ukm_len)
260         {
261
262         const struct gost_cipher_info *param=get_encryption_params(NULL);
263         EC_KEY *ephemeral = NULL;
264         GOST_KEY_TRANSPORT *gkt=NULL;
265         const EC_POINT *pub_key_point = EC_KEY_get0_public_key(EVP_PKEY_get0(pubk));
266         unsigned char shared_key[32],crypted_key[44];
267         gost_ctx ctx;
268         EVP_PKEY *newkey=NULL;
269         
270         /* Do not use vizir cipher parameters with cryptopro */
271         if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS) && param ==  gost_cipher_list)
272                 {
273                 param= gost_cipher_list+1;
274                 }       
275         ephemeral = make_ec_ephemeral_key(EVP_PKEY_get0(pubk),eph_key);
276     VKO_compute_key(shared_key,32,pub_key_point,ephemeral,ukm);
277         gost_init(&ctx,param->sblock);  
278         keyWrapCryptoPro(&ctx,shared_key,ukm,key,crypted_key);
279         gkt = GOST_KEY_TRANSPORT_new();
280         if (!gkt)
281                 {
282                 goto memerr;
283                 }       
284         if(!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv,
285                         ukm,8))
286                 {
287                 goto memerr;
288                 }       
289         if (!ASN1_OCTET_STRING_set(gkt->key_info->imit,crypted_key+40,4))
290                 {
291                 goto memerr;
292                 }
293         if (!ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key,crypted_key+8,32))
294                 {
295                 goto memerr;
296                 }
297         newkey = ec_ephemeral_key_to_EVP(pubk,NID_id_GostR3410_2001,ephemeral);
298         if (!X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,newkey))
299                 {
300                 GOSTerr(GOST_F_MAKE_RFC4490_KEYTRANSPORT_2001,GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
301                 goto err;
302                 }       
303         ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
304         gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
305         EVP_PKEY_free(newkey);
306         return gkt;
307         memerr:
308         GOSTerr(GOST_F_MAKE_RFC4490_KEYTRANSPORT_2001,
309                 GOST_R_MALLOC_FAILURE);
310         err:            
311         GOST_KEY_TRANSPORT_free(gkt);
312         return NULL;
313         }
314
315 /*  
316  * EVP_PKEY_METHOD callback encrypt  
317  * Implementation of GOST2001 key transport, cryptopo variation 
318  */
319
320 int pkey_GOST01cp_encrypt (EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len, const unsigned char *key,size_t key_len) 
321         {
322         GOST_KEY_TRANSPORT *gkt=NULL; 
323         EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
324         struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
325         unsigned char ukm[8];
326         int ret=0;
327         if (RAND_bytes(ukm,8)<=0)
328                 {
329                 GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
330                         GOST_R_RANDOM_GENERATOR_FAILURE);
331                 return 0;
332                 }       
333                 
334         if (!(gkt=make_rfc4490_keytransport_2001(pubk,gost_get_priv_key(data->eph_seckey),key, key_len,ukm,8)))
335                 {
336                 goto err;
337                 }       
338         if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,&out))>0) ret =1;
339         GOST_KEY_TRANSPORT_free(gkt);
340         return ret;     
341         err:            
342         GOST_KEY_TRANSPORT_free(gkt);
343         return -1;
344         }
345 /* Public, because it would be needed in SSL implementation */
346 int decrypt_rfc4490_shared_key_2001(EVP_PKEY *priv,GOST_KEY_TRANSPORT *gkt,
347         unsigned char *key_buf,int key_buf_len) 
348         {
349         unsigned char wrappedKey[44];
350         unsigned char sharedKey[32];
351         gost_ctx ctx;
352         const struct gost_cipher_info *param=NULL;
353         EVP_PKEY *eph_key=NULL;
354         
355         eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
356         param = get_encryption_params(gkt->key_agreement_info->cipher);
357         gost_init(&ctx,param->sblock);  
358         OPENSSL_assert(gkt->key_agreement_info->eph_iv->length==8);
359         memcpy(wrappedKey,gkt->key_agreement_info->eph_iv->data,8);
360         OPENSSL_assert(gkt->key_info->encrypted_key->length==32);
361         memcpy(wrappedKey+8,gkt->key_info->encrypted_key->data,32);
362         OPENSSL_assert(gkt->key_info->imit->length==4);
363         memcpy(wrappedKey+40,gkt->key_info->imit->data,4);      
364         VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(eph_key)),
365                 EVP_PKEY_get0(priv),wrappedKey);
366         if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key_buf))
367                 {
368                 GOSTerr(GOST_F_PKCS7_GOST94CP_KEY_TRANSPORT_DECRYPT,
369                         GOST_R_ERROR_COMPUTING_SHARED_KEY);
370                 goto err;
371                 }       
372                                 
373         EVP_PKEY_free(eph_key);
374         return 32;
375         err:
376         EVP_PKEY_free(eph_key);
377         return -1;
378         }
379 /*  
380  * EVP_PKEY_METHOD callback decrypt  
381  * Implementation of GOST2001 key transport, cryptopo variation 
382  */
383 int pkey_GOST01cp_decrypt (EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_len, const unsigned char *in, size_t in_len)
384         {
385         const unsigned char *p = in;
386         EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
387         GOST_KEY_TRANSPORT *gkt = NULL;
388         int ret=0;      
389
390         if (!key)
391                 {
392                 *key_len = 32;
393                 return 1;
394                 }       
395         gkt = d2i_GOST_KEY_TRANSPORT(NULL,(const unsigned char **)&p,
396                 in_len);
397         if (!gkt)
398                 {
399                 GOSTerr(GOST_F_PKCS7_GOST94CP_KEY_TRANSPORT_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
400                 return -1;
401                 }       
402         ret =   decrypt_rfc4490_shared_key_2001(priv,gkt,key,*key_len);
403         GOST_KEY_TRANSPORT_free(gkt);
404         return ret;
405         }