blob: bda1208af935adee4555206cc913cb9d2d5ca6df [file] [log] [blame]
Juan Castilloa57a4d52015-04-02 15:44:20 +01001/*
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +00002 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
Juan Castilloa57a4d52015-04-02 15:44:20 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Juan Castilloa57a4d52015-04-02 15:44:20 +01005 */
6
7/*
Juan Castillobae6b2a2015-11-05 09:24:53 +00008 * X509 parser based on mbed TLS
Juan Castilloa57a4d52015-04-02 15:44:20 +01009 *
10 * This module implements functions to check the integrity of a X509v3
11 * certificate ASN.1 structure and extract authentication parameters from the
12 * extensions field, such as an image hash or a public key.
13 */
14
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +000015#include <arch_helpers.h>
Juan Castilloa57a4d52015-04-02 15:44:20 +010016#include <assert.h>
17#include <img_parser_mod.h>
18#include <mbedtls_common.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <string.h>
Douglas Raillarda8954fc2017-01-26 15:54:44 +000022#include <utils.h>
Juan Castilloa57a4d52015-04-02 15:44:20 +010023
Juan Castillobae6b2a2015-11-05 09:24:53 +000024/* mbed TLS headers */
25#include <mbedtls/asn1.h>
26#include <mbedtls/oid.h>
27#include <mbedtls/platform.h>
Juan Castilloa57a4d52015-04-02 15:44:20 +010028
29/* Maximum OID string length ("a.b.c.d.e.f ...") */
30#define MAX_OID_STR_LEN 64
31
Juan Castillobae6b2a2015-11-05 09:24:53 +000032#define LIB_NAME "mbed TLS X509v3"
Juan Castilloa57a4d52015-04-02 15:44:20 +010033
34/* Temporary variables to speed up the authentication parameters search. These
35 * variables are assigned once during the integrity check and used any time an
36 * authentication parameter is requested, so we do not have to parse the image
37 * again */
Juan Castillobae6b2a2015-11-05 09:24:53 +000038static mbedtls_asn1_buf tbs;
39static mbedtls_asn1_buf v3_ext;
40static mbedtls_asn1_buf pk;
41static mbedtls_asn1_buf sig_alg;
42static mbedtls_asn1_buf signature;
Juan Castilloa57a4d52015-04-02 15:44:20 +010043
44/*
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +000045 * Clear all static temporary variables.
46 */
47static void clear_temp_vars(void)
48{
49#define ZERO_AND_CLEAN(x) \
50 do { \
Douglas Raillarda8954fc2017-01-26 15:54:44 +000051 zeromem(&x, sizeof(x)); \
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +000052 clean_dcache_range((uintptr_t)&x, sizeof(x)); \
53 } while (0);
54
55 ZERO_AND_CLEAN(tbs)
56 ZERO_AND_CLEAN(v3_ext);
57 ZERO_AND_CLEAN(pk);
58 ZERO_AND_CLEAN(sig_alg);
59 ZERO_AND_CLEAN(signature);
60
61#undef ZERO_AND_CLEAN
62}
63
64/*
Juan Castilloa57a4d52015-04-02 15:44:20 +010065 * Get X509v3 extension
66 *
67 * Global variable 'v3_ext' must point to the extensions region
68 * in the certificate. No need to check for errors since the image has passed
69 * the integrity check.
70 */
71static int get_ext(const char *oid, void **ext, unsigned int *ext_len)
72{
73 int oid_len;
74 size_t len;
75 unsigned char *end_ext_data, *end_ext_octet;
76 unsigned char *p;
77 const unsigned char *end;
78 char oid_str[MAX_OID_STR_LEN];
Juan Castillobae6b2a2015-11-05 09:24:53 +000079 mbedtls_asn1_buf extn_oid;
Juan Castilloa57a4d52015-04-02 15:44:20 +010080 int is_critical;
81
82 assert(oid != NULL);
83
84 p = v3_ext.p;
85 end = v3_ext.p + v3_ext.len;
86
Juan Castillobae6b2a2015-11-05 09:24:53 +000087 mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
88 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +010089
90 while (p < end) {
Douglas Raillarda8954fc2017-01-26 15:54:44 +000091 zeromem(&extn_oid, sizeof(extn_oid));
Juan Castilloa57a4d52015-04-02 15:44:20 +010092 is_critical = 0; /* DEFAULT FALSE */
93
Juan Castillobae6b2a2015-11-05 09:24:53 +000094 mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
95 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +010096 end_ext_data = p + len;
97
98 /* Get extension ID */
99 extn_oid.tag = *p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000100 mbedtls_asn1_get_tag(&p, end, &extn_oid.len, MBEDTLS_ASN1_OID);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100101 extn_oid.p = p;
102 p += extn_oid.len;
103
104 /* Get optional critical */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000105 mbedtls_asn1_get_bool(&p, end_ext_data, &is_critical);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100106
107 /* Extension data */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000108 mbedtls_asn1_get_tag(&p, end_ext_data, &len,
109 MBEDTLS_ASN1_OCTET_STRING);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100110 end_ext_octet = p + len;
111
112 /* Detect requested extension */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000113 oid_len = mbedtls_oid_get_numeric_string(oid_str,
114 MAX_OID_STR_LEN,
115 &extn_oid);
116 if (oid_len == MBEDTLS_ERR_OID_BUF_TOO_SMALL) {
Juan Castilloa57a4d52015-04-02 15:44:20 +0100117 return IMG_PARSER_ERR;
118 }
119 if ((oid_len == strlen(oid_str)) && !strcmp(oid, oid_str)) {
120 *ext = (void *)p;
121 *ext_len = (unsigned int)len;
122 return IMG_PARSER_OK;
123 }
124
125 /* Next */
126 p = end_ext_octet;
127 }
128
129 return IMG_PARSER_ERR_NOT_FOUND;
130}
131
132
133/*
134 * Check the integrity of the certificate ASN.1 structure.
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +0000135 *
Juan Castilloa57a4d52015-04-02 15:44:20 +0100136 * Extract the relevant data that will be used later during authentication.
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +0000137 *
138 * This function doesn't clear the static variables located on the top of this
139 * file in case of an error. It is only called from check_integrity(), which
140 * performs the cleanup if necessary.
Juan Castilloa57a4d52015-04-02 15:44:20 +0100141 */
142static int cert_parse(void *img, unsigned int img_len)
143{
144 int ret, is_critical;
145 size_t len;
146 unsigned char *p, *end, *crt_end;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000147 mbedtls_asn1_buf sig_alg1, sig_alg2;
Juan Castilloa57a4d52015-04-02 15:44:20 +0100148
149 p = (unsigned char *)img;
150 len = img_len;
151 end = p + len;
152
153 /*
154 * Certificate ::= SEQUENCE {
155 * tbsCertificate TBSCertificate,
156 * signatureAlgorithm AlgorithmIdentifier,
157 * signatureValue BIT STRING }
158 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000159 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
160 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100161 if (ret != 0) {
162 return IMG_PARSER_ERR_FORMAT;
163 }
164
165 if (len > (size_t)(end - p)) {
166 return IMG_PARSER_ERR_FORMAT;
167 }
168 crt_end = p + len;
169
170 /*
171 * TBSCertificate ::= SEQUENCE {
172 */
173 tbs.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000174 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
175 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100176 if (ret != 0) {
177 return IMG_PARSER_ERR_FORMAT;
178 }
179 end = p + len;
180 tbs.len = end - tbs.p;
181
182 /*
183 * Version ::= INTEGER { v1(0), v2(1), v3(2) }
184 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000185 ret = mbedtls_asn1_get_tag(&p, end, &len,
186 MBEDTLS_ASN1_CONTEXT_SPECIFIC |
187 MBEDTLS_ASN1_CONSTRUCTED | 0);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100188 if (ret != 0) {
189 return IMG_PARSER_ERR_FORMAT;
190 }
191 p += len;
192
193 /*
194 * CertificateSerialNumber ::= INTEGER
195 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000196 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100197 if (ret != 0) {
198 return IMG_PARSER_ERR_FORMAT;
199 }
200 p += len;
201
202 /*
203 * signature AlgorithmIdentifier
204 */
205 sig_alg1.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000206 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
207 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100208 if (ret != 0) {
209 return IMG_PARSER_ERR_FORMAT;
210 }
211 if ((end - p) < 1) {
212 return IMG_PARSER_ERR_FORMAT;
213 }
214 sig_alg1.len = (p + len) - sig_alg1.p;
215 p += len;
216
217 /*
218 * issuer Name
219 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000220 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
221 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100222 if (ret != 0) {
223 return IMG_PARSER_ERR_FORMAT;
224 }
225 p += len;
226
227 /*
228 * Validity ::= SEQUENCE {
229 * notBefore Time,
230 * notAfter Time }
231 *
232 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000233 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
234 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100235 if (ret != 0) {
236 return IMG_PARSER_ERR_FORMAT;
237 }
238 p += len;
239
240 /*
241 * subject Name
242 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000243 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
244 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100245 if (ret != 0) {
246 return IMG_PARSER_ERR_FORMAT;
247 }
248 p += len;
249
250 /*
251 * SubjectPublicKeyInfo
252 */
253 pk.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000254 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
255 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100256 if (ret != 0) {
257 return IMG_PARSER_ERR_FORMAT;
258 }
259 pk.len = (p + len) - pk.p;
260 p += len;
261
262 /*
263 * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
264 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000265 ret = mbedtls_asn1_get_tag(&p, end, &len,
266 MBEDTLS_ASN1_CONTEXT_SPECIFIC |
267 MBEDTLS_ASN1_CONSTRUCTED | 1);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100268 if (ret != 0) {
Juan Castillobae6b2a2015-11-05 09:24:53 +0000269 if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
Juan Castilloa57a4d52015-04-02 15:44:20 +0100270 return IMG_PARSER_ERR_FORMAT;
271 }
272 } else {
273 p += len;
274 }
275
276 /*
277 * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
278 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000279 ret = mbedtls_asn1_get_tag(&p, end, &len,
280 MBEDTLS_ASN1_CONTEXT_SPECIFIC |
281 MBEDTLS_ASN1_CONSTRUCTED | 2);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100282 if (ret != 0) {
Juan Castillobae6b2a2015-11-05 09:24:53 +0000283 if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
Juan Castilloa57a4d52015-04-02 15:44:20 +0100284 return IMG_PARSER_ERR_FORMAT;
285 }
286 } else {
287 p += len;
288 }
289
290 /*
291 * extensions [3] EXPLICIT Extensions OPTIONAL
292 */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000293 ret = mbedtls_asn1_get_tag(&p, end, &len,
294 MBEDTLS_ASN1_CONTEXT_SPECIFIC |
295 MBEDTLS_ASN1_CONSTRUCTED | 3);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100296 if (ret != 0) {
297 return IMG_PARSER_ERR_FORMAT;
298 }
299
300 /*
301 * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
302 */
303 v3_ext.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000304 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
305 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100306 if (ret != 0) {
307 return IMG_PARSER_ERR_FORMAT;
308 }
309 v3_ext.len = (p + len) - v3_ext.p;
310
311 /*
312 * Check extensions integrity
313 */
314 while (p < end) {
Juan Castillobae6b2a2015-11-05 09:24:53 +0000315 ret = mbedtls_asn1_get_tag(&p, end, &len,
316 MBEDTLS_ASN1_CONSTRUCTED |
317 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100318 if (ret != 0) {
319 return IMG_PARSER_ERR_FORMAT;
320 }
321
322 /* Get extension ID */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000323 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100324 if (ret != 0) {
325 return IMG_PARSER_ERR_FORMAT;
326 }
327 p += len;
328
329 /* Get optional critical */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000330 ret = mbedtls_asn1_get_bool(&p, end, &is_critical);
331 if ((ret != 0) && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) {
Juan Castilloa57a4d52015-04-02 15:44:20 +0100332 return IMG_PARSER_ERR_FORMAT;
333 }
334
335 /* Data should be octet string type */
Juan Castillobae6b2a2015-11-05 09:24:53 +0000336 ret = mbedtls_asn1_get_tag(&p, end, &len,
337 MBEDTLS_ASN1_OCTET_STRING);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100338 if (ret != 0) {
339 return IMG_PARSER_ERR_FORMAT;
340 }
341 p += len;
342 }
343
344 if (p != end) {
345 return IMG_PARSER_ERR_FORMAT;
346 }
347
348 end = crt_end;
349
350 /*
351 * }
352 * -- end of TBSCertificate
353 *
354 * signatureAlgorithm AlgorithmIdentifier
355 */
356 sig_alg2.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000357 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
358 MBEDTLS_ASN1_SEQUENCE);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100359 if (ret != 0) {
360 return IMG_PARSER_ERR_FORMAT;
361 }
362 if ((end - p) < 1) {
363 return IMG_PARSER_ERR_FORMAT;
364 }
365 sig_alg2.len = (p + len) - sig_alg2.p;
366 p += len;
367
368 /* Compare both signature algorithms */
369 if (sig_alg1.len != sig_alg2.len) {
370 return IMG_PARSER_ERR_FORMAT;
371 }
Antonio Nino Diaz0ca1afa2017-02-09 10:26:54 +0000372 if (0 != memcmp(sig_alg1.p, sig_alg2.p, sig_alg1.len)) {
Juan Castilloa57a4d52015-04-02 15:44:20 +0100373 return IMG_PARSER_ERR_FORMAT;
374 }
375 memcpy(&sig_alg, &sig_alg1, sizeof(sig_alg));
376
377 /*
378 * signatureValue BIT STRING
379 */
380 signature.p = p;
Juan Castillobae6b2a2015-11-05 09:24:53 +0000381 ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_BIT_STRING);
Juan Castilloa57a4d52015-04-02 15:44:20 +0100382 if (ret != 0) {
383 return IMG_PARSER_ERR_FORMAT;
384 }
385 signature.len = (p + len) - signature.p;
386 p += len;
387
388 /* Check certificate length */
389 if (p != end) {
390 return IMG_PARSER_ERR_FORMAT;
391 }
392
393 return IMG_PARSER_OK;
394}
395
396
397/* Exported functions */
398
399static void init(void)
400{
401 mbedtls_init();
402}
403
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +0000404/*
405 * Wrapper for cert_parse() that clears the static variables used by it in case
406 * of an error.
407 */
Juan Castilloa57a4d52015-04-02 15:44:20 +0100408static int check_integrity(void *img, unsigned int img_len)
409{
Antonio Nino Diaz30eb9672017-01-13 15:03:19 +0000410 int rc = cert_parse(img, img_len);
411
412 if (rc != IMG_PARSER_OK)
413 clear_temp_vars();
414
415 return rc;
Juan Castilloa57a4d52015-04-02 15:44:20 +0100416}
417
418/*
419 * Extract an authentication parameter from an X509v3 certificate
Juan Castillobfb7fa62016-01-22 11:05:57 +0000420 *
421 * This function returns a pointer to the extracted data and its length.
422 * Depending on the type of parameter, a pointer to the data stored in the
423 * certificate may be returned (i.e. an octet string containing a hash). Other
424 * data may need to be copied and formatted (i.e. integers). In the later case,
425 * a buffer of the correct type needs to be statically allocated, filled and
426 * returned.
Juan Castilloa57a4d52015-04-02 15:44:20 +0100427 */
428static int get_auth_param(const auth_param_type_desc_t *type_desc,
429 void *img, unsigned int img_len,
430 void **param, unsigned int *param_len)
431{
432 int rc = IMG_PARSER_OK;
433
434 /* We do not use img because the check_integrity function has already
435 * extracted the relevant data (v3_ext, pk, sig_alg, etc) */
436
437 switch (type_desc->type) {
438 case AUTH_PARAM_RAW_DATA:
439 /* Data to be signed */
440 *param = (void *)tbs.p;
441 *param_len = (unsigned int)tbs.len;
442 break;
443 case AUTH_PARAM_HASH:
Juan Castillobfb7fa62016-01-22 11:05:57 +0000444 case AUTH_PARAM_NV_CTR:
Juan Castilloa57a4d52015-04-02 15:44:20 +0100445 /* All these parameters are included as X509v3 extensions */
446 rc = get_ext(type_desc->cookie, param, param_len);
447 break;
448 case AUTH_PARAM_PUB_KEY:
449 if (type_desc->cookie != 0) {
450 /* Get public key from extension */
451 rc = get_ext(type_desc->cookie, param, param_len);
452 } else {
453 /* Get the subject public key */
454 *param = (void *)pk.p;
455 *param_len = (unsigned int)pk.len;
456 }
457 break;
458 case AUTH_PARAM_SIG_ALG:
459 /* Get the certificate signature algorithm */
460 *param = (void *)sig_alg.p;
461 *param_len = (unsigned int)sig_alg.len;
462 break;
463 case AUTH_PARAM_SIG:
464 /* Get the certificate signature */
465 *param = (void *)signature.p;
466 *param_len = (unsigned int)signature.len;
467 break;
468 default:
469 rc = IMG_PARSER_ERR_NOT_FOUND;
470 break;
471 }
472
473 return rc;
474}
475
476REGISTER_IMG_PARSER_LIB(IMG_CERT, LIB_NAME, init, \
477 check_integrity, get_auth_param);