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