Coverage Report

Created: 2024-06-20 06:28

/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
  ret = _gnutls_session_supports_group(session, group->id);
148
0
  if (ret < 0)
149
0
    return gnutls_assert_val(ret);
150
151
0
  if (pub->algo == GNUTLS_PK_GOST_12_512) {
152
0
    gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_512);
153
0
  } else {
154
0
    gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_256);
155
0
  }
156
157
0
  _gnutls_session_group_set(session, group);
158
159
0
  ret = _gnutls_pk_generate_keys(pub->algo, curve, priv, 1);
160
0
  if (ret < 0)
161
0
    return gnutls_assert_val(ret);
162
163
0
  priv->gost_params = pub->gost_params;
164
165
0
  print_priv_key(priv);
166
167
0
  session->key.key.size = 32; /* GOST key size */
168
0
  session->key.key.data = gnutls_malloc(session->key.key.size);
169
0
  if (session->key.key.data == NULL) {
170
0
    gnutls_assert();
171
0
    session->key.key.size = 0;
172
0
    return GNUTLS_E_MEMORY_ERROR;
173
0
  }
174
175
  /* Generate random */
176
0
  ret = gnutls_rnd(GNUTLS_RND_RANDOM, session->key.key.data,
177
0
       session->key.key.size);
178
0
  if (ret < 0) {
179
0
    gnutls_assert();
180
0
    gnutls_free(session->key.key.data);
181
0
    session->key.key.size = 0;
182
0
    return ret;
183
0
  }
184
185
0
  return 0;
186
0
}
187
188
/* KX message is:
189
   TLSGostKeyTransportBlob ::= SEQUENCE {
190
        keyBlob GostR3410-KeyTransport,
191
        proxyKeyBlobs SEQUENCE OF TLSProxyKeyTransportBlob OPTIONAL
192
   }
193
194
   draft-smyshlyaev-tls12-gost-suites does not define proxyKeyBlobs, but old
195
   CSPs still send additional information after keyBlob.
196
197
   We only need keyBlob and we completely ignore the rest of the structure.
198
199
   _gnutls_gost_keytrans_decrypt will decrypt GostR3410-KeyTransport
200
   */
201
202
static int proc_vko_gost_client_kx(gnutls_session_t session, uint8_t *data,
203
           size_t _data_size)
204
0
{
205
0
  int ret, i = 0;
206
0
  ssize_t data_size = _data_size;
207
0
  gnutls_privkey_t privkey = session->internals.selected_key;
208
0
  uint8_t ukm_data[MAX_HASH_SIZE];
209
0
  gnutls_datum_t ukm = { ukm_data, VKO_GOST_UKM_LEN };
210
0
  gnutls_datum_t cek;
211
0
  int len;
212
213
0
  if (!privkey || privkey->type != GNUTLS_PRIVKEY_X509)
214
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
215
216
  /* Skip TLSGostKeyTransportBlob tag and length */
217
0
  DECR_LEN(data_size, 1);
218
0
  if (data[0] != (ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED))
219
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
220
0
  i += 1;
221
222
0
  ret = asn1_get_length_der(&data[i], data_size, &len);
223
0
  if (ret < 0)
224
0
    return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
225
0
  DECR_LEN(data_size, len);
226
0
  i += len;
227
228
  /* Check that nothing is left after TLSGostKeyTransportBlob */
229
0
  DECR_LEN_FINAL(data_size, ret);
230
231
  /* Point data to GostR3410-KeyTransport */
232
0
  data_size = ret;
233
0
  data += i;
234
235
  /* Now do the tricky part: determine length of GostR3410-KeyTransport */
236
0
  DECR_LEN(data_size, 1); /* tag */
237
0
  ret = asn1_get_length_der(&data[1], data_size, &len);
238
0
  DECR_LEN_FINAL(data_size, len + ret);
239
240
0
  cek.data = data;
241
0
  cek.size = ret + len + 1;
242
243
0
  ret = calc_ukm(session, ukm_data);
244
0
  if (ret < 0)
245
0
    return gnutls_assert_val(ret);
246
247
0
  ret = _gnutls_gost_keytrans_decrypt(&privkey->key.x509->params, &cek,
248
0
              &ukm, &session->key.key);
249
0
  if (ret < 0)
250
0
    return gnutls_assert_val(ret);
251
252
0
  return 0;
253
0
}
254
255
static int gen_vko_gost_client_kx(gnutls_session_t session,
256
          gnutls_buffer_st *data)
257
0
{
258
0
  int ret;
259
0
  gnutls_datum_t out = {};
260
0
  uint8_t ukm_data[MAX_HASH_SIZE];
261
0
  gnutls_datum_t ukm = { ukm_data, VKO_GOST_UKM_LEN };
262
0
  gnutls_pk_params_st pub;
263
0
  gnutls_pk_params_st priv;
264
0
  uint8_t tl[1 + ASN1_MAX_LENGTH_SIZE];
265
0
  int len;
266
267
0
  ret = calc_ukm(session, ukm_data);
268
0
  if (ret < 0)
269
0
    return gnutls_assert_val(ret);
270
271
0
  gnutls_pk_params_init(&pub);
272
0
  gnutls_pk_params_init(&priv);
273
0
  ret = vko_prepare_client_keys(session, &pub, &priv);
274
0
  if (ret < 0)
275
0
    return gnutls_assert_val(ret);
276
277
0
  ret = _gnutls_gost_keytrans_encrypt(&pub, &priv, &session->key.key,
278
0
              &ukm, &out);
279
0
  if (ret < 0) {
280
0
    gnutls_assert();
281
0
    goto cleanup;
282
0
  }
283
284
0
  tl[0] = ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED;
285
0
  asn1_length_der(out.size, tl + 1, &len);
286
0
  ret = gnutls_buffer_append_data(data, tl, len + 1);
287
0
  if (ret < 0) {
288
0
    gnutls_assert();
289
0
    goto cleanup;
290
0
  }
291
292
0
  ret = gnutls_buffer_append_data(data, out.data, out.size);
293
0
  if (ret < 0) {
294
0
    gnutls_assert();
295
0
    goto cleanup;
296
0
  }
297
298
0
  ret = data->length;
299
0
cleanup:
300
  /* no longer needed */
301
0
  gnutls_pk_params_release(&priv);
302
0
  gnutls_pk_params_release(&pub);
303
304
0
  _gnutls_free_datum(&out);
305
306
0
  return ret;
307
0
}
308
#endif