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