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 <http://www.gnu.org/licenses/>
19 */
20
21 /*
22 * This is split from main TLS key exchange, because it might be useful in
23 * future for S/MIME support. For the definition of the algorithm see RFC 4357,
24 * section 5.2.
25 */
26 #include "gnutls_int.h"
27 #include "vko.h"
28 #include "pk.h"
29 #include "common.h"
30
31 static int
_gnutls_gost_vko_key(gnutls_pk_params_st * pub,gnutls_pk_params_st * priv,gnutls_datum_t * ukm,gnutls_digest_algorithm_t digalg,gnutls_datum_t * kek)32 _gnutls_gost_vko_key(gnutls_pk_params_st *pub,
33 gnutls_pk_params_st *priv,
34 gnutls_datum_t *ukm,
35 gnutls_digest_algorithm_t digalg,
36 gnutls_datum_t *kek)
37 {
38 gnutls_datum_t tmp_vko_key;
39 int ret;
40
41 ret = _gnutls_pk_derive_nonce(pub->algo, &tmp_vko_key,
42 priv, pub, ukm);
43 if (ret < 0)
44 return gnutls_assert_val(ret);
45
46 kek->size = gnutls_hash_get_len(digalg);
47 kek->data = gnutls_malloc(kek->size);
48 if (kek->data == NULL) {
49 gnutls_assert();
50 ret = GNUTLS_E_MEMORY_ERROR;
51 goto cleanup;
52 }
53
54 ret = gnutls_hash_fast(digalg, tmp_vko_key.data, tmp_vko_key.size, kek->data);
55 if (ret < 0) {
56 gnutls_assert();
57 _gnutls_free_datum(kek);
58 goto cleanup;
59 }
60
61 ret = 0;
62
63 cleanup:
64 _gnutls_free_temp_key_datum(&tmp_vko_key);
65
66 return ret;
67 }
68
69 static const gnutls_datum_t zero_data = { NULL, 0 };
70
71 int
_gnutls_gost_keytrans_encrypt(gnutls_pk_params_st * pub,gnutls_pk_params_st * priv,gnutls_datum_t * cek,gnutls_datum_t * ukm,gnutls_datum_t * out)72 _gnutls_gost_keytrans_encrypt(gnutls_pk_params_st *pub,
73 gnutls_pk_params_st *priv,
74 gnutls_datum_t *cek,
75 gnutls_datum_t *ukm,
76 gnutls_datum_t *out)
77 {
78 int ret;
79 gnutls_datum_t kek;
80 gnutls_datum_t enc, imit;
81 gnutls_digest_algorithm_t digalg;
82 ASN1_TYPE kx;
83
84 if (pub->algo == GNUTLS_PK_GOST_01)
85 digalg = GNUTLS_DIG_GOSTR_94;
86 else
87 digalg = GNUTLS_DIG_STREEBOG_256;
88
89 ret = _gnutls_gost_vko_key(pub, priv, ukm, digalg, &kek);
90 if (ret < 0) {
91 gnutls_assert();
92
93 return ret;
94 }
95
96 ret = _gnutls_gost_key_wrap(pub->gost_params, &kek, ukm, cek,
97 &enc, &imit);
98 _gnutls_free_key_datum(&kek);
99 if (ret < 0) {
100 gnutls_assert();
101
102 return ret;
103 }
104
105 ret = asn1_create_element(_gnutls_get_gnutls_asn(),
106 "GNUTLS.GostR3410-KeyTransport",
107 &kx);
108 if (ret != ASN1_SUCCESS) {
109 gnutls_assert();
110 ret = _gnutls_asn2err(ret);
111 _gnutls_free_datum(&enc);
112 _gnutls_free_datum(&imit);
113
114 return ret;
115 }
116
117 ret = _gnutls_x509_write_value(kx, "transportParameters.ukm", ukm);
118 if (ret < 0) {
119 gnutls_assert();
120 goto cleanup;
121 }
122
123 ret = _gnutls_x509_encode_and_copy_PKI_params(kx,
124 "transportParameters.ephemeralPublicKey",
125 priv);
126 if (ret < 0) {
127 gnutls_assert();
128 goto cleanup;
129 }
130
131 if ((ret = asn1_write_value(kx, "transportParameters.encryptionParamSet",
132 gnutls_gost_paramset_get_oid(pub->gost_params),
133 1)) != ASN1_SUCCESS) {
134 gnutls_assert();
135 ret = _gnutls_asn2err(ret);
136 goto cleanup;
137 }
138
139 ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.encryptedKey", &enc);
140 if (ret < 0) {
141 gnutls_assert();
142 goto cleanup;
143 }
144
145 ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.maskKey", &zero_data);
146 if (ret < 0) {
147 gnutls_assert();
148 goto cleanup;
149 }
150 ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.macKey", &imit);
151 if (ret < 0) {
152 gnutls_assert();
153 goto cleanup;
154 }
155
156 ret = _gnutls_x509_der_encode(kx, "", out, 0);
157 if (ret < 0) {
158 gnutls_assert();
159 goto cleanup;
160 }
161
162 ret = 0;
163
164 cleanup:
165 asn1_delete_structure(&kx);
166 _gnutls_free_datum(&enc);
167 _gnutls_free_datum(&imit);
168
169 return ret;
170 }
171
172 int
_gnutls_gost_keytrans_decrypt(gnutls_pk_params_st * priv,gnutls_datum_t * cek,gnutls_datum_t * ukm,gnutls_datum_t * out)173 _gnutls_gost_keytrans_decrypt(gnutls_pk_params_st *priv,
174 gnutls_datum_t *cek,
175 gnutls_datum_t *ukm,
176 gnutls_datum_t *out)
177 {
178 int ret;
179 ASN1_TYPE kx;
180 gnutls_pk_params_st pub;
181 gnutls_datum_t kek;
182 gnutls_datum_t ukm2, enc, imit;
183 char oid[MAX_OID_SIZE];
184 int oid_size;
185 gnutls_digest_algorithm_t digalg;
186
187 if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(),
188 "GNUTLS.GostR3410-KeyTransport",
189 &kx)) != ASN1_SUCCESS) {
190 gnutls_assert();
191 ret = _gnutls_asn2err(ret);
192
193 return ret;
194 }
195
196 ret = _asn1_strict_der_decode(&kx, cek->data, cek->size, NULL);
197 if (ret != ASN1_SUCCESS) {
198 gnutls_assert();
199 ret = _gnutls_asn2err(ret);
200 asn1_delete_structure(&kx);
201
202 return ret;
203 }
204
205 ret = _gnutls_get_asn_mpis(kx,
206 "transportParameters.ephemeralPublicKey",
207 &pub);
208 if (ret < 0) {
209 gnutls_assert();
210 goto cleanup;
211 }
212
213 if (pub.algo != priv->algo ||
214 pub.gost_params != priv->gost_params ||
215 pub.curve != priv->curve) {
216 gnutls_assert();
217 ret = GNUTLS_E_ILLEGAL_PARAMETER;
218 goto cleanup;
219 }
220
221 oid_size = sizeof(oid);
222 ret = asn1_read_value(kx, "transportParameters.encryptionParamSet", oid, &oid_size);
223 if (ret != ASN1_SUCCESS) {
224 gnutls_assert();
225 ret = _gnutls_asn2err(ret);
226 goto cleanup;
227 }
228
229 if (gnutls_oid_to_gost_paramset(oid) != priv->gost_params) {
230 gnutls_assert();
231 ret = GNUTLS_E_ASN1_DER_ERROR;
232 goto cleanup;
233 }
234
235 ret = _gnutls_x509_read_value(kx, "transportParameters.ukm", &ukm2);
236 if (ret < 0) {
237 gnutls_assert();
238 goto cleanup;
239 }
240
241 /* Kind of strange design. For TLS UKM is calculated as a hash of
242 * client and server random. At the same time UKM is transmitted as a
243 * part of KeyTransport structure. At this point we have to compare
244 * them to check that they are equal. This does not result in an oracle
245 * of any kind as all values are transmitted in cleartext. Returning
246 * that this point won't give any information to the attacker.
247 */
248 if (ukm2.size != ukm->size || memcmp(ukm2.data, ukm->data, ukm->size) != 0) {
249 gnutls_assert();
250 _gnutls_free_datum(&ukm2);
251 ret = GNUTLS_E_DECRYPTION_FAILED;
252 goto cleanup;
253 }
254 _gnutls_free_datum(&ukm2);
255
256 ret = _gnutls_x509_read_value(kx, "sessionEncryptedKey.encryptedKey",
257 &enc);
258 if (ret < 0) {
259 gnutls_assert();
260 goto cleanup;
261 }
262
263 ret = _gnutls_x509_read_value(kx, "sessionEncryptedKey.macKey",
264 &imit);
265 if (ret < 0) {
266 gnutls_assert();
267 _gnutls_free_datum(&enc);
268 goto cleanup;
269 }
270
271 if (pub.algo == GNUTLS_PK_GOST_01)
272 digalg = GNUTLS_DIG_GOSTR_94;
273 else
274 digalg = GNUTLS_DIG_STREEBOG_256;
275
276 ret = _gnutls_gost_vko_key(&pub, priv, ukm, digalg, &kek);
277 if (ret < 0) {
278 gnutls_assert();
279 goto cleanup2;
280 }
281
282 ret = _gnutls_gost_key_unwrap(pub.gost_params, &kek, ukm,
283 &enc, &imit, out);
284 _gnutls_free_key_datum(&kek);
285
286 if (ret < 0) {
287 gnutls_assert();
288 goto cleanup2;
289 }
290
291 ret = 0;
292
293 cleanup2:
294 _gnutls_free_datum(&imit);
295 _gnutls_free_datum(&enc);
296 cleanup:
297 gnutls_pk_params_release(&pub);
298 asn1_delete_structure(&kx);
299
300 return ret;
301 }
302