Line data Source code
1 : /*
2 : * Copyright (C) 2011-2012 Free Software Foundation, Inc.
3 : * Copyright (C) 2016 Dmitry Eremin-Solenikov
4 : *
5 : * This file is part of GnuTLS.
6 : *
7 : * The GnuTLS is free software; you can redistribute it and/or
8 : * modify it under the terms of the GNU Lesser General Public License
9 : * as published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * This library is distributed in the hope that it will be useful, but
13 : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : * Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public License
18 : * along with this program. If not, see <http://www.gnu.org/licenses/>
19 : *
20 : */
21 :
22 : #include "gnutls_int.h"
23 : #include "auth.h"
24 : #include "errors.h"
25 : #include "vko.h"
26 : #include <state.h>
27 : #include <datum.h>
28 : #include <ext/signature.h>
29 : #include <ext/supported_groups.h>
30 : #include <auth/cert.h>
31 : #include <pk.h>
32 : #include <abstract_int.h>
33 :
34 : #if defined(ENABLE_GOST)
35 : static int gen_vko_gost_client_kx(gnutls_session_t, gnutls_buffer_st *);
36 : static int proc_vko_gost_client_kx(gnutls_session_t session,
37 : uint8_t * data, size_t _data_size);
38 :
39 : /* VKO GOST Key Exchange:
40 : * see draft-smyshlyaev-tls12-gost-suites-06, Section 4.2.4
41 : *
42 : * Client generates ephemeral key pair, uses server's public key (from
43 : * certificate), ephemeral private key and additional nonce (UKM) to generate
44 : * (VKO) shared point/shared secret. This secret is used to encrypt (key wrap)
45 : * random PMS. Then encrypted PMS and client's ephemeral public key are wrappen
46 : * in ASN.1 structure and sent in KX message.
47 : *
48 : * Server uses decodes ASN.1 structure and uses it's own private key and
49 : * client's ephemeral public key to unwrap PMS.
50 : *
51 : * Note, this KX is not PFS one, despite using ephemeral key pairs on client
52 : * side.
53 : */
54 : const mod_auth_st vko_gost_auth_struct = {
55 : "VKO_GOST",
56 : _gnutls_gen_cert_server_crt,
57 : _gnutls_gen_cert_client_crt,
58 : NULL,
59 : gen_vko_gost_client_kx,
60 : _gnutls_gen_cert_client_crt_vrfy,
61 : _gnutls_gen_cert_server_cert_req,
62 :
63 : _gnutls_proc_crt,
64 : _gnutls_proc_crt,
65 : NULL,
66 : proc_vko_gost_client_kx,
67 : _gnutls_proc_cert_client_crt_vrfy,
68 : _gnutls_proc_cert_cert_req
69 : };
70 :
71 : #define VKO_GOST_UKM_LEN 8
72 :
73 : static int
74 74 : calc_ukm(gnutls_session_t session, uint8_t *ukm)
75 : {
76 74 : gnutls_digest_algorithm_t digalg = GNUTLS_DIG_STREEBOG_256;
77 74 : gnutls_hash_hd_t dig;
78 74 : int ret;
79 :
80 74 : ret = gnutls_hash_init(&dig, digalg);
81 74 : if (ret < 0)
82 0 : return gnutls_assert_val(ret);
83 :
84 74 : gnutls_hash(dig, session->security_parameters.client_random,
85 : sizeof(session->security_parameters.client_random));
86 :
87 74 : gnutls_hash(dig, session->security_parameters.server_random,
88 : sizeof(session->security_parameters.server_random));
89 :
90 74 : gnutls_hash_deinit(dig, ukm);
91 :
92 74 : return gnutls_hash_get_len(digalg);
93 : }
94 :
95 37 : static int print_priv_key(gnutls_pk_params_st *params)
96 : {
97 37 : int ret;
98 37 : uint8_t priv_buf[512/8];
99 37 : char buf[512 / 4 + 1];
100 37 : size_t bytes = sizeof(priv_buf);
101 :
102 : /* Check if _gnutls_hard_log will print anything */
103 37 : if (likely(_gnutls_log_level < 9))
104 : return GNUTLS_E_SUCCESS;
105 :
106 0 : ret = _gnutls_mpi_print(params->params[GOST_K],
107 : priv_buf, &bytes);
108 0 : if (ret < 0)
109 0 : return gnutls_assert_val(ret);
110 :
111 0 : _gnutls_hard_log("INT: VKO PRIVATE KEY[%zd]: %s\n",
112 : bytes, _gnutls_bin2hex(priv_buf,
113 : bytes,
114 : buf, sizeof(buf),
115 : NULL));
116 : return 0;
117 : }
118 :
119 : static int
120 37 : vko_prepare_client_keys(gnutls_session_t session,
121 : gnutls_pk_params_st *pub,
122 : gnutls_pk_params_st *priv)
123 : {
124 37 : int ret;
125 37 : gnutls_ecc_curve_t curve;
126 37 : const gnutls_group_entry_st *group;
127 37 : cert_auth_info_t info;
128 37 : gnutls_pcert_st peer_cert;
129 :
130 37 : info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
131 37 : if (info == NULL || info->ncerts == 0)
132 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
133 :
134 37 : ret = _gnutls_get_auth_info_pcert(&peer_cert,
135 : session->security_parameters.
136 : server_ctype, info);
137 37 : if (ret < 0)
138 0 : return gnutls_assert_val(ret);
139 :
140 : /* Copy public key contents and free the rest */
141 37 : memcpy(pub, &peer_cert.pubkey->params, sizeof(gnutls_pk_params_st));
142 37 : gnutls_free(peer_cert.pubkey);
143 37 : peer_cert.pubkey = NULL;
144 37 : gnutls_pcert_deinit(&peer_cert);
145 :
146 37 : curve = pub->curve;
147 37 : group = _gnutls_id_to_group(_gnutls_ecc_curve_get_group(curve));
148 37 : if (group == NULL) {
149 0 : _gnutls_debug_log("received unknown curve %d\n", curve);
150 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
151 : } else {
152 37 : _gnutls_debug_log("received curve %s\n", group->name);
153 : }
154 :
155 37 : ret = _gnutls_session_supports_group(session, group->id);
156 37 : if (ret < 0)
157 0 : return gnutls_assert_val(ret);
158 :
159 37 : if (pub->algo == GNUTLS_PK_GOST_12_512) {
160 29 : gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_512);
161 : } else {
162 8 : gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_256);
163 : }
164 :
165 37 : _gnutls_session_group_set(session, group);
166 :
167 37 : ret = _gnutls_pk_generate_keys(pub->algo,
168 : curve,
169 : priv, 1);
170 37 : if (ret < 0)
171 0 : return gnutls_assert_val(ret);
172 :
173 37 : priv->gost_params = pub->gost_params;
174 :
175 37 : print_priv_key(priv);
176 :
177 37 : session->key.key.size = 32; /* GOST key size */
178 37 : session->key.key.data = gnutls_malloc(session->key.key.size);
179 37 : if (session->key.key.data == NULL) {
180 0 : gnutls_assert();
181 0 : session->key.key.size = 0;
182 0 : return GNUTLS_E_MEMORY_ERROR;
183 : }
184 :
185 : /* Generate random */
186 74 : ret = gnutls_rnd(GNUTLS_RND_RANDOM, session->key.key.data,
187 37 : session->key.key.size);
188 37 : if (ret < 0) {
189 0 : gnutls_assert();
190 0 : gnutls_free(session->key.key.data);
191 0 : session->key.key.size = 0;
192 0 : return ret;
193 : }
194 :
195 : return 0;
196 : }
197 :
198 : /* KX message is:
199 : TLSGostKeyTransportBlob ::= SEQUENCE {
200 : keyBlob GostR3410-KeyTransport,
201 : proxyKeyBlobs SEQUENCE OF TLSProxyKeyTransportBlob OPTIONAL
202 : }
203 :
204 : draft-smyshlyaev-tls12-gost-suites does not define proxyKeyBlobs, but old
205 : CSPs still send additional information after keyBlob.
206 :
207 : We only need keyBlob and we completely ignore the rest of the structure.
208 :
209 : _gnutls_gost_keytrans_decrypt will decrypt GostR3410-KeyTransport
210 : */
211 :
212 : static int
213 37 : proc_vko_gost_client_kx(gnutls_session_t session,
214 : uint8_t * data, size_t _data_size)
215 : {
216 37 : int ret, i = 0;
217 37 : ssize_t data_size = _data_size;
218 37 : gnutls_privkey_t privkey = session->internals.selected_key;
219 37 : uint8_t ukm_data[MAX_HASH_SIZE];
220 37 : gnutls_datum_t ukm = {ukm_data, VKO_GOST_UKM_LEN};
221 37 : gnutls_datum_t cek;
222 37 : int len;
223 :
224 37 : if (!privkey || privkey->type != GNUTLS_PRIVKEY_X509)
225 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
226 :
227 : /* Skip TLSGostKeyTransportBlob tag and length */
228 37 : DECR_LEN(data_size, 1);
229 37 : if (data[0] != (ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED))
230 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
231 37 : i += 1;
232 :
233 37 : ret = asn1_get_length_der(&data[i], data_size, &len);
234 37 : if (ret < 0)
235 0 : return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
236 37 : DECR_LEN(data_size, len);
237 37 : i += len;
238 :
239 : /* Check that nothing is left after TLSGostKeyTransportBlob */
240 37 : DECR_LEN_FINAL(data_size, ret);
241 :
242 : /* Point data to GostR3410-KeyTransport */
243 37 : data_size = ret;
244 37 : data += i;
245 :
246 : /* Now do the tricky part: determine length of GostR3410-KeyTransport */
247 37 : DECR_LEN(data_size, 1); /* tag */
248 37 : ret = asn1_get_length_der(&data[1], data_size, &len);
249 37 : DECR_LEN_FINAL(data_size, len + ret);
250 :
251 37 : cek.data = data;
252 37 : cek.size = ret + len + 1;
253 :
254 37 : ret = calc_ukm(session, ukm_data);
255 37 : if (ret < 0)
256 0 : return gnutls_assert_val(ret);
257 :
258 37 : ret = _gnutls_gost_keytrans_decrypt(&privkey->key.x509->params,
259 : &cek, &ukm,
260 : &session->key.key);
261 37 : if (ret < 0)
262 0 : return gnutls_assert_val(ret);
263 :
264 : return 0;
265 : }
266 :
267 : static int
268 37 : gen_vko_gost_client_kx(gnutls_session_t session,
269 : gnutls_buffer_st * data)
270 : {
271 37 : int ret;
272 37 : gnutls_datum_t out = {};
273 37 : uint8_t ukm_data[MAX_HASH_SIZE];
274 37 : gnutls_datum_t ukm = {ukm_data, VKO_GOST_UKM_LEN};
275 37 : gnutls_pk_params_st pub;
276 37 : gnutls_pk_params_st priv;
277 37 : uint8_t tl[1 + ASN1_MAX_LENGTH_SIZE];
278 37 : int len;
279 :
280 37 : ret = calc_ukm(session, ukm_data);
281 37 : if (ret < 0)
282 0 : return gnutls_assert_val(ret);
283 :
284 37 : gnutls_pk_params_init(&pub);
285 37 : gnutls_pk_params_init(&priv);
286 37 : ret = vko_prepare_client_keys(session, &pub, &priv);
287 37 : if (ret < 0)
288 0 : return gnutls_assert_val(ret);
289 :
290 37 : ret = _gnutls_gost_keytrans_encrypt(&pub,
291 : &priv,
292 : &session->key.key,
293 : &ukm, &out);
294 37 : if (ret < 0) {
295 0 : gnutls_assert();
296 0 : goto cleanup;
297 : }
298 :
299 37 : tl[0] = ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED;
300 37 : asn1_length_der(out.size, tl + 1, &len);
301 37 : ret = gnutls_buffer_append_data(data, tl, len + 1);
302 37 : if (ret < 0) {
303 0 : gnutls_assert();
304 0 : goto cleanup;
305 : }
306 :
307 37 : ret = gnutls_buffer_append_data(data, out.data, out.size);
308 37 : if (ret < 0) {
309 0 : gnutls_assert();
310 0 : goto cleanup;
311 : }
312 :
313 37 : ret = data->length;
314 37 : cleanup:
315 : /* no longer needed */
316 37 : gnutls_pk_params_release(&priv);
317 37 : gnutls_pk_params_release(&pub);
318 :
319 37 : _gnutls_free_datum(&out);
320 :
321 37 : return ret;
322 : }
323 : #endif
|