Coverage Report

Created: 2023-03-26 08:33

/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