1fd01744364889a0e6eaa576a2491f63a421020a
[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 #include "gost2001_keyx.h"
20
21 /* Implementation of CryptoPro VKO 34.10-2001 algorithm */
22 static int VKO_compute_key(unsigned char *shared_key, size_t shared_key_size,
23                            const EC_POINT *pub_key, EC_KEY *priv_key,
24                            const unsigned char *ukm)
25 {
26     unsigned char ukm_be[8], databuf[64], hashbuf[64];
27     BIGNUM *UKM = NULL, *p = NULL, *order = NULL, *X = NULL, *Y = NULL;
28     const BIGNUM *key = EC_KEY_get0_private_key(priv_key);
29     EC_POINT *pnt = EC_POINT_new(EC_KEY_get0_group(priv_key));
30     int i;
31     gost_hash_ctx hash_ctx;
32     BN_CTX *ctx = BN_CTX_new();
33
34     for (i = 0; i < 8; i++) {
35         ukm_be[7 - i] = ukm[i];
36     }
37     BN_CTX_start(ctx);
38     UKM = BN_bin2bn(ukm_be, 8, NULL);
39     p = BN_CTX_get(ctx);
40     order = BN_CTX_get(ctx);
41     X = BN_CTX_get(ctx);
42     Y = BN_CTX_get(ctx);
43     EC_GROUP_get_order(EC_KEY_get0_group(priv_key), order, ctx);
44     BN_mod_mul(p, key, UKM, order, ctx);
45     EC_POINT_mul(EC_KEY_get0_group(priv_key), pnt, NULL, pub_key, p, ctx);
46     EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
47                                         pnt, X, Y, ctx);
48     /*
49      * Serialize elliptic curve point same way as we do it when saving key
50      */
51     store_bignum(Y, databuf, 32);
52     store_bignum(X, databuf + 32, 32);
53     /* And reverse byte order of whole buffer */
54     for (i = 0; i < 64; i++) {
55         hashbuf[63 - i] = databuf[i];
56     }
57     init_gost_hash_ctx(&hash_ctx, &GostR3411_94_CryptoProParamSet);
58     start_hash(&hash_ctx);
59     hash_block(&hash_ctx, hashbuf, 64);
60     finish_hash(&hash_ctx, shared_key);
61     done_gost_hash_ctx(&hash_ctx);
62     BN_free(UKM);
63     BN_CTX_end(ctx);
64     BN_CTX_free(ctx);
65     EC_POINT_free(pnt);
66     return 32;
67 }
68
69 /*
70  * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
71  * algorithm
72  */
73 int pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
74                          size_t *keylen)
75 {
76     /*
77      * Public key of peer in the ctx field peerkey Our private key in the ctx
78      * pkey ukm is in the algorithm specific context data
79      */
80     EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
81     EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
82     struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
83
84     if (!data->shared_ukm) {
85         GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
86         return 0;
87     }
88
89     if (key == NULL) {
90         *keylen = 32;
91         return 32;
92     }
93
94     *keylen =
95         VKO_compute_key(key, 32,
96                         EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
97                         (EC_KEY *)EVP_PKEY_get0(my_key), data->shared_ukm);
98     return 1;
99 }
100
101 /*
102  * EVP_PKEY_METHOD callback encrypt
103  * Implementation of GOST2001 key transport, cryptocom variation
104  */
105 /*
106  * Generates ephemeral key based on pubk algorithm computes shared key using
107  * VKO and returns filled up GOST_KEY_TRANSPORT structure
108  */
109
110 /*
111  * EVP_PKEY_METHOD callback encrypt
112  * Implementation of GOST2001 key transport, cryptopo variation
113  */
114
115 int pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out,
116                           size_t *out_len, const unsigned char *key,
117                           size_t key_len)
118 {
119     GOST_KEY_TRANSPORT *gkt = NULL;
120     EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
121     struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
122     const struct gost_cipher_info *param = get_encryption_params(NULL);
123     unsigned char ukm[8], shared_key[32], crypted_key[44];
124     int ret = 0;
125     int key_is_ephemeral = 1;
126     gost_ctx cctx;
127     EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx);
128     if (data->shared_ukm) {
129         memcpy(ukm, data->shared_ukm, 8);
130     } else if (out) {
131
132         if (RAND_bytes(ukm, 8) <= 0) {
133             GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
134                     GOST_R_RANDOM_GENERATOR_FAILURE);
135             return 0;
136         }
137     }
138     /* Check for private key in the peer_key of context */
139     if (sec_key) {
140         key_is_ephemeral = 0;
141         if (!gost_get0_priv_key(sec_key)) {
142             GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
143                     GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
144             goto err;
145         }
146     } else {
147         key_is_ephemeral = 1;
148         if (out) {
149             sec_key = EVP_PKEY_new();
150             EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), EC_KEY_new());
151             EVP_PKEY_copy_parameters(sec_key, pubk);
152             if (!gost2001_keygen(EVP_PKEY_get0(sec_key))) {
153                 goto err;
154             }
155         }
156     }
157     if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS)
158         && param == gost_cipher_list) {
159         param = gost_cipher_list + 1;
160     }
161     if (out) {
162         VKO_compute_key(shared_key, 32,
163                         EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),
164                         EVP_PKEY_get0(sec_key), ukm);
165         gost_init(&cctx, param->sblock);
166         keyWrapCryptoPro(&cctx, shared_key, ukm, key, crypted_key);
167     }
168     gkt = GOST_KEY_TRANSPORT_new();
169     if (!gkt) {
170         goto err;
171     }
172     if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8)) {
173         goto err;
174     }
175     if (!ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4)) {
176         goto err;
177     }
178     if (!ASN1_OCTET_STRING_set
179         (gkt->key_info->encrypted_key, crypted_key + 8, 32)) {
180         goto err;
181     }
182     if (key_is_ephemeral) {
183         if (!X509_PUBKEY_set
184             (&gkt->key_agreement_info->ephem_key, out ? sec_key : pubk)) {
185             GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
186                     GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
187             goto err;
188         }
189     }
190     ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
191     gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
192     if (key_is_ephemeral)
193         EVP_PKEY_free(sec_key);
194     if (!key_is_ephemeral) {
195         /* Set control "public key from client certificate used" */
196         if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
197             <= 0) {
198             GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, GOST_R_CTRL_CALL_FAILED);
199             goto err;
200         }
201     }
202     if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt, out ? &out : NULL)) > 0)
203         ret = 1;
204     GOST_KEY_TRANSPORT_free(gkt);
205     return ret;
206  err:
207     if (key_is_ephemeral)
208         EVP_PKEY_free(sec_key);
209     GOST_KEY_TRANSPORT_free(gkt);
210     return -1;
211 }
212
213 /*
214  * EVP_PKEY_METHOD callback decrypt
215  * Implementation of GOST2001 key transport, cryptopo variation
216  */
217 int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key,
218                           size_t *key_len, const unsigned char *in,
219                           size_t in_len)
220 {
221     const unsigned char *p = in;
222     EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
223     GOST_KEY_TRANSPORT *gkt = NULL;
224     int ret = 0;
225     unsigned char wrappedKey[44];
226     unsigned char sharedKey[32];
227     gost_ctx ctx;
228     const struct gost_cipher_info *param = NULL;
229     EVP_PKEY *eph_key = NULL, *peerkey = NULL;
230
231     if (!key) {
232         *key_len = 32;
233         return 1;
234     }
235     gkt = d2i_GOST_KEY_TRANSPORT(NULL, (const unsigned char **)&p, in_len);
236     if (!gkt) {
237         GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
238                 GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
239         return -1;
240     }
241
242     /* If key transport structure contains public key, use it */
243     eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
244     if (eph_key) {
245         if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) {
246             GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
247                     GOST_R_INCOMPATIBLE_PEER_KEY);
248             goto err;
249         }
250     } else {
251         /* Set control "public key from client certificate used" */
252         if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
253             <= 0) {
254             GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_CTRL_CALL_FAILED);
255             goto err;
256         }
257     }
258     peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
259     if (!peerkey) {
260         GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_NO_PEER_KEY);
261         goto err;
262     }
263
264     param = get_encryption_params(gkt->key_agreement_info->cipher);
265     if (!param) {
266         goto err;
267     }
268
269     gost_init(&ctx, param->sblock);
270     OPENSSL_assert(gkt->key_agreement_info->eph_iv->length == 8);
271     memcpy(wrappedKey, gkt->key_agreement_info->eph_iv->data, 8);
272     OPENSSL_assert(gkt->key_info->encrypted_key->length == 32);
273     memcpy(wrappedKey + 8, gkt->key_info->encrypted_key->data, 32);
274     OPENSSL_assert(gkt->key_info->imit->length == 4);
275     memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4);
276     VKO_compute_key(sharedKey, 32,
277                     EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
278                     EVP_PKEY_get0(priv), wrappedKey);
279     if (!keyUnwrapCryptoPro(&ctx, sharedKey, wrappedKey, key)) {
280         GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
281                 GOST_R_ERROR_COMPUTING_SHARED_KEY);
282         goto err;
283     }
284
285     ret = 1;
286  err:
287     EVP_PKEY_free(eph_key);
288     GOST_KEY_TRANSPORT_free(gkt);
289     return ret;
290 }