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/* Implementation of CryptoPro VKO 34.10-2001 algorithm */
22280297Sjkimstatic int VKO_compute_key(unsigned char *shared_key, size_t shared_key_size,
23280297Sjkim                           const EC_POINT *pub_key, EC_KEY *priv_key,
24280297Sjkim                           const unsigned char *ukm)
25280297Sjkim{
26280297Sjkim    unsigned char ukm_be[8], databuf[64], hashbuf[64];
27280297Sjkim    BIGNUM *UKM = NULL, *p = NULL, *order = NULL, *X = NULL, *Y = NULL;
28280297Sjkim    const BIGNUM *key = EC_KEY_get0_private_key(priv_key);
29280297Sjkim    EC_POINT *pnt = EC_POINT_new(EC_KEY_get0_group(priv_key));
30280297Sjkim    int i;
31280297Sjkim    gost_hash_ctx hash_ctx;
32280297Sjkim    BN_CTX *ctx = BN_CTX_new();
33238384Sjkim
34280297Sjkim    for (i = 0; i < 8; i++) {
35280297Sjkim        ukm_be[7 - i] = ukm[i];
36280297Sjkim    }
37280297Sjkim    BN_CTX_start(ctx);
38280297Sjkim    UKM = getbnfrombuf(ukm_be, 8);
39280297Sjkim    p = BN_CTX_get(ctx);
40280297Sjkim    order = BN_CTX_get(ctx);
41280297Sjkim    X = BN_CTX_get(ctx);
42280297Sjkim    Y = BN_CTX_get(ctx);
43280297Sjkim    EC_GROUP_get_order(EC_KEY_get0_group(priv_key), order, ctx);
44280297Sjkim    BN_mod_mul(p, key, UKM, order, ctx);
45280297Sjkim    EC_POINT_mul(EC_KEY_get0_group(priv_key), pnt, NULL, pub_key, p, ctx);
46280297Sjkim    EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
47280297Sjkim                                        pnt, X, Y, ctx);
48280297Sjkim    /*
49280297Sjkim     * Serialize elliptic curve point same way as we do it when saving key
50280297Sjkim     */
51280297Sjkim    store_bignum(Y, databuf, 32);
52280297Sjkim    store_bignum(X, databuf + 32, 32);
53280297Sjkim    /* And reverse byte order of whole buffer */
54280297Sjkim    for (i = 0; i < 64; i++) {
55280297Sjkim        hashbuf[63 - i] = databuf[i];
56280297Sjkim    }
57280297Sjkim    init_gost_hash_ctx(&hash_ctx, &GostR3411_94_CryptoProParamSet);
58280297Sjkim    start_hash(&hash_ctx);
59280297Sjkim    hash_block(&hash_ctx, hashbuf, 64);
60280297Sjkim    finish_hash(&hash_ctx, shared_key);
61280297Sjkim    done_gost_hash_ctx(&hash_ctx);
62280297Sjkim    BN_free(UKM);
63280297Sjkim    BN_CTX_end(ctx);
64280297Sjkim    BN_CTX_free(ctx);
65280297Sjkim    EC_POINT_free(pnt);
66280297Sjkim    return 32;
67280297Sjkim}
68238384Sjkim
69238384Sjkim/*
70238384Sjkim * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
71238384Sjkim * algorithm
72238384Sjkim */
73280297Sjkimint pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
74280297Sjkim                         size_t *keylen)
75238384Sjkim{
76280297Sjkim    /*
77280297Sjkim     * Public key of peer in the ctx field peerkey Our private key in the ctx
78280297Sjkim     * pkey ukm is in the algorithm specific context data
79280297Sjkim     */
80280297Sjkim    EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
81280297Sjkim    EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
82280297Sjkim    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
83238384Sjkim
84280297Sjkim    if (!data->shared_ukm) {
85280297Sjkim        GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
86280297Sjkim        return 0;
87280297Sjkim    }
88238384Sjkim
89280297Sjkim    if (key == NULL) {
90280297Sjkim        *keylen = 32;
91280297Sjkim        return 32;
92280297Sjkim    }
93238384Sjkim
94280297Sjkim    *keylen =
95280297Sjkim        VKO_compute_key(key, 32,
96280297Sjkim                        EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
97280297Sjkim                        (EC_KEY *)EVP_PKEY_get0(my_key), data->shared_ukm);
98280297Sjkim    return 1;
99280297Sjkim}
100238384Sjkim
101280297Sjkim/*
102280297Sjkim * EVP_PKEY_METHOD callback encrypt
103280297Sjkim * Implementation of GOST2001 key transport, cryptocom variation
104238384Sjkim */
105280297Sjkim/*
106280297Sjkim * Generates ephemeral key based on pubk algorithm computes shared key using
107280297Sjkim * VKO and returns filled up GOST_KEY_TRANSPORT structure
108238384Sjkim */
109238384Sjkim
110280297Sjkim/*
111280297Sjkim * EVP_PKEY_METHOD callback encrypt
112280297Sjkim * Implementation of GOST2001 key transport, cryptopo variation
113238384Sjkim */
114238384Sjkim
115280297Sjkimint pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out,
116280297Sjkim                          size_t *out_len, const unsigned char *key,
117280297Sjkim                          size_t key_len)
118280297Sjkim{
119280297Sjkim    GOST_KEY_TRANSPORT *gkt = NULL;
120280297Sjkim    EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
121280297Sjkim    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
122280297Sjkim    const struct gost_cipher_info *param = get_encryption_params(NULL);
123280297Sjkim    unsigned char ukm[8], shared_key[32], crypted_key[44];
124280297Sjkim    int ret = 0;
125280297Sjkim    int key_is_ephemeral = 1;
126280297Sjkim    gost_ctx cctx;
127280297Sjkim    EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx);
128280297Sjkim    if (data->shared_ukm) {
129280297Sjkim        memcpy(ukm, data->shared_ukm, 8);
130280297Sjkim    } else if (out) {
131280297Sjkim
132280297Sjkim        if (RAND_bytes(ukm, 8) <= 0) {
133280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
134280297Sjkim                    GOST_R_RANDOM_GENERATOR_FAILURE);
135280297Sjkim            return 0;
136280297Sjkim        }
137280297Sjkim    }
138280297Sjkim    /* Check for private key in the peer_key of context */
139280297Sjkim    if (sec_key) {
140280297Sjkim        key_is_ephemeral = 0;
141280297Sjkim        if (!gost_get0_priv_key(sec_key)) {
142280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
143280297Sjkim                    GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
144280297Sjkim            goto err;
145280297Sjkim        }
146280297Sjkim    } else {
147280297Sjkim        key_is_ephemeral = 1;
148280297Sjkim        if (out) {
149280297Sjkim            sec_key = EVP_PKEY_new();
150306195Sjkim            if (sec_key == NULL)
151306195Sjkim                goto err;
152280297Sjkim            EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), EC_KEY_new());
153280297Sjkim            EVP_PKEY_copy_parameters(sec_key, pubk);
154280297Sjkim            if (!gost2001_keygen(EVP_PKEY_get0(sec_key))) {
155280297Sjkim                goto err;
156280297Sjkim            }
157280297Sjkim        }
158280297Sjkim    }
159280297Sjkim    if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS)
160280297Sjkim        && param == gost_cipher_list) {
161280297Sjkim        param = gost_cipher_list + 1;
162280297Sjkim    }
163280297Sjkim    if (out) {
164280297Sjkim        VKO_compute_key(shared_key, 32,
165280297Sjkim                        EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),
166280297Sjkim                        EVP_PKEY_get0(sec_key), ukm);
167280297Sjkim        gost_init(&cctx, param->sblock);
168280297Sjkim        keyWrapCryptoPro(&cctx, shared_key, ukm, key, crypted_key);
169280297Sjkim    }
170280297Sjkim    gkt = GOST_KEY_TRANSPORT_new();
171280297Sjkim    if (!gkt) {
172280297Sjkim        goto err;
173280297Sjkim    }
174280297Sjkim    if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8)) {
175280297Sjkim        goto err;
176280297Sjkim    }
177280297Sjkim    if (!ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4)) {
178280297Sjkim        goto err;
179280297Sjkim    }
180280297Sjkim    if (!ASN1_OCTET_STRING_set
181280297Sjkim        (gkt->key_info->encrypted_key, crypted_key + 8, 32)) {
182280297Sjkim        goto err;
183280297Sjkim    }
184280297Sjkim    if (key_is_ephemeral) {
185280297Sjkim        if (!X509_PUBKEY_set
186280297Sjkim            (&gkt->key_agreement_info->ephem_key, out ? sec_key : pubk)) {
187280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
188280297Sjkim                    GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
189280297Sjkim            goto err;
190280297Sjkim        }
191280297Sjkim    }
192280297Sjkim    ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
193280297Sjkim    gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
194280297Sjkim    if (key_is_ephemeral && sec_key)
195280297Sjkim        EVP_PKEY_free(sec_key);
196280297Sjkim    if (!key_is_ephemeral) {
197280297Sjkim        /* Set control "public key from client certificate used" */
198280297Sjkim        if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
199280297Sjkim            <= 0) {
200280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, GOST_R_CTRL_CALL_FAILED);
201280297Sjkim            goto err;
202280297Sjkim        }
203280297Sjkim    }
204280297Sjkim    if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt, out ? &out : NULL)) > 0)
205280297Sjkim        ret = 1;
206280297Sjkim    GOST_KEY_TRANSPORT_free(gkt);
207280297Sjkim    return ret;
208280297Sjkim err:
209280297Sjkim    if (key_is_ephemeral && sec_key)
210280297Sjkim        EVP_PKEY_free(sec_key);
211280297Sjkim    GOST_KEY_TRANSPORT_free(gkt);
212280297Sjkim    return -1;
213280297Sjkim}
214280297Sjkim
215280297Sjkim/*
216280297Sjkim * EVP_PKEY_METHOD callback decrypt
217280297Sjkim * Implementation of GOST2001 key transport, cryptopo variation
218238384Sjkim */
219280297Sjkimint pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key,
220280297Sjkim                          size_t *key_len, const unsigned char *in,
221280297Sjkim                          size_t in_len)
222280297Sjkim{
223280297Sjkim    const unsigned char *p = in;
224280297Sjkim    EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
225280297Sjkim    GOST_KEY_TRANSPORT *gkt = NULL;
226280297Sjkim    int ret = 0;
227280297Sjkim    unsigned char wrappedKey[44];
228280297Sjkim    unsigned char sharedKey[32];
229280297Sjkim    gost_ctx ctx;
230280297Sjkim    const struct gost_cipher_info *param = NULL;
231280297Sjkim    EVP_PKEY *eph_key = NULL, *peerkey = NULL;
232238384Sjkim
233280297Sjkim    if (!key) {
234280297Sjkim        *key_len = 32;
235280297Sjkim        return 1;
236280297Sjkim    }
237280297Sjkim    gkt = d2i_GOST_KEY_TRANSPORT(NULL, (const unsigned char **)&p, in_len);
238280297Sjkim    if (!gkt) {
239280297Sjkim        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
240280297Sjkim                GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
241280297Sjkim        return -1;
242280297Sjkim    }
243238384Sjkim
244280297Sjkim    /* If key transport structure contains public key, use it */
245280297Sjkim    eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
246280297Sjkim    if (eph_key) {
247280297Sjkim        if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) {
248280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
249280297Sjkim                    GOST_R_INCOMPATIBLE_PEER_KEY);
250280297Sjkim            goto err;
251280297Sjkim        }
252280297Sjkim    } else {
253280297Sjkim        /* Set control "public key from client certificate used" */
254280297Sjkim        if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
255280297Sjkim            <= 0) {
256280297Sjkim            GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_CTRL_CALL_FAILED);
257280297Sjkim            goto err;
258280297Sjkim        }
259280297Sjkim    }
260280297Sjkim    peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
261280297Sjkim    if (!peerkey) {
262280297Sjkim        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_NO_PEER_KEY);
263238384Sjkim        goto err;
264238384Sjkim    }
265238384Sjkim
266280297Sjkim    param = get_encryption_params(gkt->key_agreement_info->cipher);
267280297Sjkim    if (!param) {
268280297Sjkim        goto err;
269280297Sjkim    }
270280297Sjkim
271280297Sjkim    gost_init(&ctx, param->sblock);
272280297Sjkim    OPENSSL_assert(gkt->key_agreement_info->eph_iv->length == 8);
273280297Sjkim    memcpy(wrappedKey, gkt->key_agreement_info->eph_iv->data, 8);
274280297Sjkim    OPENSSL_assert(gkt->key_info->encrypted_key->length == 32);
275280297Sjkim    memcpy(wrappedKey + 8, gkt->key_info->encrypted_key->data, 32);
276280297Sjkim    OPENSSL_assert(gkt->key_info->imit->length == 4);
277280297Sjkim    memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4);
278280297Sjkim    VKO_compute_key(sharedKey, 32,
279280297Sjkim                    EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
280280297Sjkim                    EVP_PKEY_get0(priv), wrappedKey);
281280297Sjkim    if (!keyUnwrapCryptoPro(&ctx, sharedKey, wrappedKey, key)) {
282280297Sjkim        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
283280297Sjkim                GOST_R_ERROR_COMPUTING_SHARED_KEY);
284280297Sjkim        goto err;
285280297Sjkim    }
286280297Sjkim
287280297Sjkim    ret = 1;
288280297Sjkim err:
289280297Sjkim    if (eph_key)
290280297Sjkim        EVP_PKEY_free(eph_key);
291280297Sjkim    if (gkt)
292280297Sjkim        GOST_KEY_TRANSPORT_free(gkt);
293280297Sjkim    return ret;
294280297Sjkim}
295