aboutsummaryrefslogtreecommitdiffstats
path: root/main/strongswan/CVE-2018-16151-CVE-2018-16152.patch
blob: 63446a1a18b22ccdc68472b82e8053d3df0cacc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
From 511563ce763b1e98e3fb3b3e3addfef550ff99b2 Mon Sep 17 00:00:00 2001
From: Tobias Brunner <tobias@strongswan.org>
Date: Tue, 28 Aug 2018 11:26:24 +0200
Subject: [PATCH] gmp: Don't parse PKCS1 v1.5 RSA signatures to verify them

Instead we generate the expected signature encoding and compare it to the
decrypted value.

Due to the lenient nature of the previous parsing code (minimum padding
length was not enforced, the algorithmIdentifier/OID parser accepts arbitrary
data after OIDs and in the parameters field etc.) it was susceptible to
Daniel Bleichenbacher's low-exponent attack (from 2006!), which allowed
forging signatures for keys that use low public exponents (i.e. e=3).

Since the public exponent is usually set to 0x10001 (65537) since quite a
while, the flaws in the previous code should not have had that much of a
practical impact in recent years.

Fixes: CVE-2018-16151, CVE-2018-16152
---
 .../plugins/gmp/gmp_rsa_private_key.c              |  66 +++++----
 src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c | 156 ++-------------------
 2 files changed, 52 insertions(+), 170 deletions(-)

diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
index 241ef7d3b12b..edc178e96a4f 100644
--- a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
+++ b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
@@ -264,14 +264,15 @@ static chunk_t rsasp1(private_gmp_rsa_private_key_t *this, chunk_t data)
 }
 
 /**
- * Build a signature using the PKCS#1 EMSA scheme
+ * Hashes the data and builds the plaintext signature value with EMSA
+ * PKCS#1 v1.5 padding.
+ *
+ * Allocates the signature data.
  */
-static bool build_emsa_pkcs1_signature(private_gmp_rsa_private_key_t *this,
-									   hash_algorithm_t hash_algorithm,
-									   chunk_t data, chunk_t *signature)
+bool gmp_emsa_pkcs1_signature_data(hash_algorithm_t hash_algorithm,
+								   chunk_t data, size_t keylen, chunk_t *em)
 {
 	chunk_t digestInfo = chunk_empty;
-	chunk_t em;
 
 	if (hash_algorithm != HASH_UNKNOWN)
 	{
@@ -295,43 +296,56 @@ static bool build_emsa_pkcs1_signature(private_gmp_rsa_private_key_t *this,
 		/* build DER-encoded digestInfo */
 		digestInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
 						asn1_algorithmIdentifier(hash_oid),
-						asn1_simple_object(ASN1_OCTET_STRING, hash)
-					  );
-		chunk_free(&hash);
+						asn1_wrap(ASN1_OCTET_STRING, "m", hash));
+
 		data = digestInfo;
 	}
 
-	if (data.len > this->k - 3)
+	if (data.len > keylen - 11)
 	{
-		free(digestInfo.ptr);
-		DBG1(DBG_LIB, "unable to sign %d bytes using a %dbit key", data.len,
-			 mpz_sizeinbase(this->n, 2));
+		chunk_free(&digestInfo);
+		DBG1(DBG_LIB, "signature value of %zu bytes is too long for key of "
+			 "%zu bytes", data.len, keylen);
 		return FALSE;
 	}
 
-	/* build chunk to rsa-decrypt:
-	 * EM = 0x00 || 0x01 || PS || 0x00 || T.
-	 * PS = 0xFF padding, with length to fill em
+	/* EM = 0x00 || 0x01 || PS || 0x00 || T.
+	 * PS = 0xFF padding, with length to fill em (at least 8 bytes)
 	 * T = encoded_hash
 	 */
-	em.len = this->k;
-	em.ptr = malloc(em.len);
+	*em = chunk_alloc(keylen);
 
 	/* fill em with padding */
-	memset(em.ptr, 0xFF, em.len);
+	memset(em->ptr, 0xFF, em->len);
 	/* set magic bytes */
-	*(em.ptr) = 0x00;
-	*(em.ptr+1) = 0x01;
-	*(em.ptr + em.len - data.len - 1) = 0x00;
-	/* set DER-encoded hash */
-	memcpy(em.ptr + em.len - data.len, data.ptr, data.len);
+	*(em->ptr) = 0x00;
+	*(em->ptr+1) = 0x01;
+	*(em->ptr + em->len - data.len - 1) = 0x00;
+	/* set encoded hash */
+	memcpy(em->ptr + em->len - data.len, data.ptr, data.len);
+
+	chunk_clear(&digestInfo);
+	return TRUE;
+}
+
+/**
+ * Build a signature using the PKCS#1 EMSA scheme
+ */
+static bool build_emsa_pkcs1_signature(private_gmp_rsa_private_key_t *this,
+									   hash_algorithm_t hash_algorithm,
+									   chunk_t data, chunk_t *signature)
+{
+	chunk_t em;
+
+	if (!gmp_emsa_pkcs1_signature_data(hash_algorithm, data, this->k, &em))
+	{
+		return FALSE;
+	}
 
 	/* build signature */
 	*signature = rsasp1(this, em);
 
-	free(digestInfo.ptr);
-	free(em.ptr);
-
+	chunk_free(&em);
 	return TRUE;
 }
 
diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c
index 52bc9fb38046..ce9a80fadf2a 100644
--- a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c
+++ b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c
@@ -70,7 +70,9 @@ struct private_gmp_rsa_public_key_t {
 /**
  * Shared functions defined in gmp_rsa_private_key.c
  */
-extern chunk_t gmp_mpz_to_chunk(const mpz_t value);
+chunk_t gmp_mpz_to_chunk(const mpz_t value);
+bool gmp_emsa_pkcs1_signature_data(hash_algorithm_t hash_algorithm,
+								   chunk_t data, size_t keylen, chunk_t *em);
 
 /**
  * RSAEP algorithm specified in PKCS#1.
@@ -115,26 +117,13 @@ static chunk_t rsavp1(private_gmp_rsa_public_key_t *this, chunk_t data)
 }
 
 /**
- * ASN.1 definition of digestInfo
- */
-static const asn1Object_t digestInfoObjects[] = {
-	{ 0, "digestInfo",			ASN1_SEQUENCE,		ASN1_OBJ  }, /*  0 */
-	{ 1,   "digestAlgorithm",	ASN1_EOC,			ASN1_RAW  }, /*  1 */
-	{ 1,   "digest",			ASN1_OCTET_STRING,	ASN1_BODY }, /*  2 */
-	{ 0, "exit",				ASN1_EOC,			ASN1_EXIT }
-};
-#define DIGEST_INFO					0
-#define DIGEST_INFO_ALGORITHM		1
-#define DIGEST_INFO_DIGEST			2
-
-/**
  * Verification of an EMSA PKCS1 signature described in PKCS#1
  */
 static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this,
 										hash_algorithm_t algorithm,
 										chunk_t data, chunk_t signature)
 {
-	chunk_t em_ori, em;
+	chunk_t em_expected, em;
 	bool success = FALSE;
 
 	/* remove any preceding 0-bytes from signature */
@@ -148,140 +137,19 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this,
 		return FALSE;
 	}
 
-	/* unpack signature */
-	em_ori = em = rsavp1(this, signature);
-
-	/* result should look like this:
-	 * EM = 0x00 || 0x01 || PS || 0x00 || T.
-	 * PS = 0xFF padding, with length to fill em
-	 * T = oid || hash
-	 */
-
-	/* check magic bytes */
-	if (em.len < 2 || *(em.ptr) != 0x00 || *(em.ptr+1) != 0x01)
+	/* generate expected signature value */
+	if (!gmp_emsa_pkcs1_signature_data(algorithm, data, this->k, &em_expected))
 	{
-		goto end;
-	}
-	em = chunk_skip(em, 2);
-
-	/* find magic 0x00 */
-	while (em.len > 0)
-	{
-		if (*em.ptr == 0x00)
-		{
-			/* found magic byte, stop */
-			em = chunk_skip(em, 1);
-			break;
-		}
-		else if (*em.ptr != 0xFF)
-		{
-			/* bad padding, decryption failed ?!*/
-			goto end;
-		}
-		em = chunk_skip(em, 1);
-	}
-
-	if (em.len == 0)
-	{
-		/* no digestInfo found */
-		goto end;
-	}
-
-	if (algorithm == HASH_UNKNOWN)
-	{   /* IKEv1 signatures without digestInfo */
-		if (em.len != data.len)
-		{
-			DBG1(DBG_LIB, "hash size in signature is %u bytes instead of"
-				 " %u bytes", em.len, data.len);
-			goto end;
-		}
-		success = memeq_const(em.ptr, data.ptr, data.len);
+		return FALSE;
 	}
-	else
-	{   /* IKEv2 and X.509 certificate signatures */
-		asn1_parser_t *parser;
-		chunk_t object;
-		int objectID;
-		hash_algorithm_t hash_algorithm = HASH_UNKNOWN;
-
-		DBG2(DBG_LIB, "signature verification:");
-		parser = asn1_parser_create(digestInfoObjects, em);
 
-		while (parser->iterate(parser, &objectID, &object))
-		{
-			switch (objectID)
-			{
-				case DIGEST_INFO:
-				{
-					if (em.len > object.len)
-					{
-						DBG1(DBG_LIB, "digestInfo field in signature is"
-							 " followed by %u surplus bytes",
-							 em.len - object.len);
-						goto end_parser;
-					}
-					break;
-				}
-				case DIGEST_INFO_ALGORITHM:
-				{
-					int hash_oid = asn1_parse_algorithmIdentifier(object,
-										 parser->get_level(parser)+1, NULL);
-
-					hash_algorithm = hasher_algorithm_from_oid(hash_oid);
-					if (hash_algorithm == HASH_UNKNOWN || hash_algorithm != algorithm)
-					{
-						DBG1(DBG_LIB, "expected hash algorithm %N, but found"
-							 " %N (OID: %#B)", hash_algorithm_names, algorithm,
-							 hash_algorithm_names, hash_algorithm,  &object);
-						goto end_parser;
-					}
-					break;
-				}
-				case DIGEST_INFO_DIGEST:
-				{
-					chunk_t hash;
-					hasher_t *hasher;
-
-					hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm);
-					if (hasher == NULL)
-					{
-						DBG1(DBG_LIB, "hash algorithm %N not supported",
-							 hash_algorithm_names, hash_algorithm);
-						goto end_parser;
-					}
-
-					if (object.len != hasher->get_hash_size(hasher))
-					{
-						DBG1(DBG_LIB, "hash size in signature is %u bytes"
-							 " instead of %u bytes", object.len,
-							 hasher->get_hash_size(hasher));
-						hasher->destroy(hasher);
-						goto end_parser;
-					}
-
-					/* build our own hash and compare */
-					if (!hasher->allocate_hash(hasher, data, &hash))
-					{
-						hasher->destroy(hasher);
-						goto end_parser;
-					}
-					hasher->destroy(hasher);
-					success = memeq_const(object.ptr, hash.ptr, hash.len);
-					free(hash.ptr);
-					break;
-				}
-				default:
-					break;
-			}
-		}
+	/* unpack signature */
+	em = rsavp1(this, signature);
 
-end_parser:
-		success &= parser->success(parser);
-		parser->destroy(parser);
-	}
+	success = chunk_equals_const(em_expected, em);
 
-end:
-	free(em_ori.ptr);
+	chunk_free(&em_expected);
+	chunk_free(&em);
 	return success;
 }
 
-- 
2.7.4