Coverage Report

Created: 2025-03-06 07:58

/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