/src/gnutls/lib/auth/vko_gost.c
Line | Count | Source (jump to first uncovered line) |
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 <https://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 its 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 | 0 | # define VKO_GOST_UKM_LEN 8 |
72 | | |
73 | | static int calc_ukm(gnutls_session_t session, uint8_t * ukm) |
74 | 0 | { |
75 | 0 | gnutls_digest_algorithm_t digalg = GNUTLS_DIG_STREEBOG_256; |
76 | 0 | gnutls_hash_hd_t dig; |
77 | 0 | int ret; |
78 | |
|
79 | 0 | ret = gnutls_hash_init(&dig, digalg); |
80 | 0 | if (ret < 0) |
81 | 0 | return gnutls_assert_val(ret); |
82 | | |
83 | 0 | gnutls_hash(dig, session->security_parameters.client_random, |
84 | 0 | sizeof(session->security_parameters.client_random)); |
85 | |
|
86 | 0 | gnutls_hash(dig, session->security_parameters.server_random, |
87 | 0 | sizeof(session->security_parameters.server_random)); |
88 | |
|
89 | 0 | gnutls_hash_deinit(dig, ukm); |
90 | |
|
91 | 0 | return gnutls_hash_get_len(digalg); |
92 | 0 | } |
93 | | |
94 | | static int print_priv_key(gnutls_pk_params_st * params) |
95 | 0 | { |
96 | 0 | int ret; |
97 | 0 | uint8_t priv_buf[512 / 8]; |
98 | 0 | char buf[512 / 4 + 1]; |
99 | 0 | size_t bytes = sizeof(priv_buf); |
100 | | |
101 | | /* Check if _gnutls_hard_log will print anything */ |
102 | 0 | if (likely(_gnutls_log_level < 9)) |
103 | 0 | return GNUTLS_E_SUCCESS; |
104 | | |
105 | 0 | ret = _gnutls_mpi_print(params->params[GOST_K], priv_buf, &bytes); |
106 | 0 | if (ret < 0) |
107 | 0 | return gnutls_assert_val(ret); |
108 | | |
109 | 0 | _gnutls_hard_log("INT: VKO PRIVATE KEY[%zd]: %s\n", |
110 | 0 | bytes, _gnutls_bin2hex(priv_buf, |
111 | 0 | bytes, buf, sizeof(buf), NULL)); |
112 | 0 | return 0; |
113 | 0 | } |
114 | | |
115 | | static int |
116 | | vko_prepare_client_keys(gnutls_session_t session, |
117 | | gnutls_pk_params_st * pub, gnutls_pk_params_st * priv) |
118 | 0 | { |
119 | 0 | int ret; |
120 | 0 | gnutls_ecc_curve_t curve; |
121 | 0 | const gnutls_group_entry_st *group; |
122 | 0 | cert_auth_info_t info; |
123 | 0 | gnutls_pcert_st peer_cert; |
124 | |
|
125 | 0 | info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); |
126 | 0 | if (info == NULL || info->ncerts == 0) |
127 | 0 | return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); |
128 | | |
129 | 0 | ret = _gnutls_get_auth_info_pcert(&peer_cert, |
130 | 0 | session->security_parameters. |
131 | 0 | server_ctype, info); |
132 | 0 | if (ret < 0) |
133 | 0 | return gnutls_assert_val(ret); |
134 | | |
135 | | /* Copy public key contents and free the rest */ |
136 | 0 | memcpy(pub, &peer_cert.pubkey->params, sizeof(gnutls_pk_params_st)); |
137 | 0 | gnutls_free(peer_cert.pubkey); |
138 | 0 | peer_cert.pubkey = NULL; |
139 | 0 | gnutls_pcert_deinit(&peer_cert); |
140 | |
|
141 | 0 | curve = pub->curve; |
142 | 0 | group = _gnutls_id_to_group(_gnutls_ecc_curve_get_group(curve)); |
143 | 0 | if (group == NULL) { |
144 | 0 | _gnutls_debug_log("received unknown curve %d\n", curve); |
145 | 0 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
146 | 0 | } else { |
147 | 0 | _gnutls_debug_log("received curve %s\n", group->name); |
148 | 0 | } |
149 | | |
150 | 0 | ret = _gnutls_session_supports_group(session, group->id); |
151 | 0 | if (ret < 0) |
152 | 0 | return gnutls_assert_val(ret); |
153 | | |
154 | 0 | if (pub->algo == GNUTLS_PK_GOST_12_512) { |
155 | 0 | gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_512); |
156 | 0 | } else { |
157 | 0 | gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_256); |
158 | 0 | } |
159 | |
|
160 | 0 | _gnutls_session_group_set(session, group); |
161 | |
|
162 | 0 | ret = _gnutls_pk_generate_keys(pub->algo, curve, priv, 1); |
163 | 0 | if (ret < 0) |
164 | 0 | return gnutls_assert_val(ret); |
165 | | |
166 | 0 | priv->gost_params = pub->gost_params; |
167 | |
|
168 | 0 | print_priv_key(priv); |
169 | |
|
170 | 0 | session->key.key.size = 32; /* GOST key size */ |
171 | 0 | session->key.key.data = gnutls_malloc(session->key.key.size); |
172 | 0 | if (session->key.key.data == NULL) { |
173 | 0 | gnutls_assert(); |
174 | 0 | session->key.key.size = 0; |
175 | 0 | return GNUTLS_E_MEMORY_ERROR; |
176 | 0 | } |
177 | | |
178 | | /* Generate random */ |
179 | 0 | ret = gnutls_rnd(GNUTLS_RND_RANDOM, session->key.key.data, |
180 | 0 | session->key.key.size); |
181 | 0 | if (ret < 0) { |
182 | 0 | gnutls_assert(); |
183 | 0 | gnutls_free(session->key.key.data); |
184 | 0 | session->key.key.size = 0; |
185 | 0 | return ret; |
186 | 0 | } |
187 | | |
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | | /* KX message is: |
192 | | TLSGostKeyTransportBlob ::= SEQUENCE { |
193 | | keyBlob GostR3410-KeyTransport, |
194 | | proxyKeyBlobs SEQUENCE OF TLSProxyKeyTransportBlob OPTIONAL |
195 | | } |
196 | | |
197 | | draft-smyshlyaev-tls12-gost-suites does not define proxyKeyBlobs, but old |
198 | | CSPs still send additional information after keyBlob. |
199 | | |
200 | | We only need keyBlob and we completely ignore the rest of the structure. |
201 | | |
202 | | _gnutls_gost_keytrans_decrypt will decrypt GostR3410-KeyTransport |
203 | | */ |
204 | | |
205 | | static int |
206 | | proc_vko_gost_client_kx(gnutls_session_t session, |
207 | | uint8_t * data, size_t _data_size) |
208 | 0 | { |
209 | 0 | int ret, i = 0; |
210 | 0 | ssize_t data_size = _data_size; |
211 | 0 | gnutls_privkey_t privkey = session->internals.selected_key; |
212 | 0 | uint8_t ukm_data[MAX_HASH_SIZE]; |
213 | 0 | gnutls_datum_t ukm = { ukm_data, VKO_GOST_UKM_LEN }; |
214 | 0 | gnutls_datum_t cek; |
215 | 0 | int len; |
216 | |
|
217 | 0 | if (!privkey || privkey->type != GNUTLS_PRIVKEY_X509) |
218 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
219 | | |
220 | | /* Skip TLSGostKeyTransportBlob tag and length */ |
221 | 0 | DECR_LEN(data_size, 1); |
222 | 0 | if (data[0] != (ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED)) |
223 | 0 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
224 | 0 | i += 1; |
225 | |
|
226 | 0 | ret = asn1_get_length_der(&data[i], data_size, &len); |
227 | 0 | if (ret < 0) |
228 | 0 | return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR); |
229 | 0 | DECR_LEN(data_size, len); |
230 | 0 | i += len; |
231 | | |
232 | | /* Check that nothing is left after TLSGostKeyTransportBlob */ |
233 | 0 | DECR_LEN_FINAL(data_size, ret); |
234 | | |
235 | | /* Point data to GostR3410-KeyTransport */ |
236 | 0 | data_size = ret; |
237 | 0 | data += i; |
238 | | |
239 | | /* Now do the tricky part: determine length of GostR3410-KeyTransport */ |
240 | 0 | DECR_LEN(data_size, 1); /* tag */ |
241 | 0 | ret = asn1_get_length_der(&data[1], data_size, &len); |
242 | 0 | DECR_LEN_FINAL(data_size, len + ret); |
243 | | |
244 | 0 | cek.data = data; |
245 | 0 | cek.size = ret + len + 1; |
246 | |
|
247 | 0 | ret = calc_ukm(session, ukm_data); |
248 | 0 | if (ret < 0) |
249 | 0 | return gnutls_assert_val(ret); |
250 | | |
251 | 0 | ret = _gnutls_gost_keytrans_decrypt(&privkey->key.x509->params, |
252 | 0 | &cek, &ukm, &session->key.key); |
253 | 0 | if (ret < 0) |
254 | 0 | return gnutls_assert_val(ret); |
255 | | |
256 | 0 | return 0; |
257 | 0 | } |
258 | | |
259 | | static int |
260 | | gen_vko_gost_client_kx(gnutls_session_t session, gnutls_buffer_st * data) |
261 | 0 | { |
262 | 0 | int ret; |
263 | 0 | gnutls_datum_t out = { }; |
264 | 0 | uint8_t ukm_data[MAX_HASH_SIZE]; |
265 | 0 | gnutls_datum_t ukm = { ukm_data, VKO_GOST_UKM_LEN }; |
266 | 0 | gnutls_pk_params_st pub; |
267 | 0 | gnutls_pk_params_st priv; |
268 | 0 | uint8_t tl[1 + ASN1_MAX_LENGTH_SIZE]; |
269 | 0 | int len; |
270 | |
|
271 | 0 | ret = calc_ukm(session, ukm_data); |
272 | 0 | if (ret < 0) |
273 | 0 | return gnutls_assert_val(ret); |
274 | | |
275 | 0 | gnutls_pk_params_init(&pub); |
276 | 0 | gnutls_pk_params_init(&priv); |
277 | 0 | ret = vko_prepare_client_keys(session, &pub, &priv); |
278 | 0 | if (ret < 0) |
279 | 0 | return gnutls_assert_val(ret); |
280 | | |
281 | 0 | ret = _gnutls_gost_keytrans_encrypt(&pub, |
282 | 0 | &priv, |
283 | 0 | &session->key.key, &ukm, &out); |
284 | 0 | if (ret < 0) { |
285 | 0 | gnutls_assert(); |
286 | 0 | goto cleanup; |
287 | 0 | } |
288 | | |
289 | 0 | tl[0] = ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED; |
290 | 0 | asn1_length_der(out.size, tl + 1, &len); |
291 | 0 | ret = gnutls_buffer_append_data(data, tl, len + 1); |
292 | 0 | if (ret < 0) { |
293 | 0 | gnutls_assert(); |
294 | 0 | goto cleanup; |
295 | 0 | } |
296 | | |
297 | 0 | ret = gnutls_buffer_append_data(data, out.data, out.size); |
298 | 0 | if (ret < 0) { |
299 | 0 | gnutls_assert(); |
300 | 0 | goto cleanup; |
301 | 0 | } |
302 | | |
303 | 0 | ret = data->length; |
304 | 0 | cleanup: |
305 | | /* no longer needed */ |
306 | 0 | gnutls_pk_params_release(&priv); |
307 | 0 | gnutls_pk_params_release(&pub); |
308 | |
|
309 | 0 | _gnutls_free_datum(&out); |
310 | |
|
311 | 0 | return ret; |
312 | 0 | } |
313 | | #endif |