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