blob: df59961b0ac18b07c386355e853f031199fd3a56 [file] [log] [blame]
Juan Castillo11abdcd2014-10-21 11:30:42 +01001/*
Masahiro Yamadaa27c1662017-05-22 12:11:24 +09002 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
Juan Castillo11abdcd2014-10-21 11:30:42 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Juan Castillo11abdcd2014-10-21 11:30:42 +01005 */
6
Juan Castillo212f7382015-12-15 16:37:57 +00007#include <assert.h>
8#include <ctype.h>
Juan Castillo11abdcd2014-10-21 11:30:42 +01009#include <getopt.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <openssl/conf.h>
15#include <openssl/engine.h>
16#include <openssl/err.h>
17#include <openssl/pem.h>
18#include <openssl/sha.h>
19#include <openssl/x509v3.h>
20
Masahiro Yamadaa27c1662017-05-22 12:11:24 +090021#if USE_TBBR_DEFS
22#include <tbbr_oid.h>
23#else
24#include <platform_oid.h>
25#endif
26
Juan Castillo11abdcd2014-10-21 11:30:42 +010027#include "cert.h"
Juan Castillo1218dd52015-07-03 16:23:16 +010028#include "cmd_opt.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010029#include "debug.h"
30#include "ext.h"
31#include "key.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010032#include "sha.h"
Juan Castilloe6d30e92015-06-12 11:27:59 +010033#include "tbbr/tbb_cert.h"
Isla Mitchell99305012017-07-11 14:54:08 +010034#include "tbbr/tbb_ext.h"
Juan Castilloe6d30e92015-06-12 11:27:59 +010035#include "tbbr/tbb_key.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010036
37/*
38 * Helper macros to simplify the code. This macro assigns the return value of
39 * the 'fn' function to 'v' and exits if the value is NULL.
40 */
41#define CHECK_NULL(v, fn) \
42 do { \
43 v = fn; \
44 if (v == NULL) { \
45 ERROR("NULL object at %s:%d\n", __FILE__, __LINE__); \
46 exit(1); \
47 } \
48 } while (0)
49
50/*
51 * This macro assigns the NID corresponding to 'oid' to 'v' and exits if the
52 * NID is undefined.
53 */
54#define CHECK_OID(v, oid) \
55 do { \
56 v = OBJ_txt2nid(oid); \
57 if (v == NID_undef) { \
58 ERROR("Cannot find TBB extension %s\n", oid); \
59 exit(1); \
60 } \
61 } while (0)
62
63#define MAX_FILENAME_LEN 1024
64#define VAL_DAYS 7300
65#define ID_TO_BIT_MASK(id) (1 << id)
Juan Castillof9f39c32015-06-01 16:34:23 +010066#define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0])))
Juan Castillo212f7382015-12-15 16:37:57 +000067#define HELP_OPT_MAX_LEN 128
Juan Castillo11abdcd2014-10-21 11:30:42 +010068
69/* Global options */
Juan Castillof9f39c32015-06-01 16:34:23 +010070static int key_alg;
Juan Castillo11abdcd2014-10-21 11:30:42 +010071static int new_keys;
72static int save_keys;
73static int print_cert;
Juan Castillo11abdcd2014-10-21 11:30:42 +010074
Juan Castillo11abdcd2014-10-21 11:30:42 +010075/* Info messages created in the Makefile */
76extern const char build_msg[];
77extern const char platform_msg[];
78
79
80static char *strdup(const char *str)
81{
82 int n = strlen(str) + 1;
83 char *dup = malloc(n);
84 if (dup) {
85 strcpy(dup, str);
86 }
87 return dup;
88}
89
Juan Castillof9f39c32015-06-01 16:34:23 +010090static const char *key_algs_str[] = {
91 [KEY_ALG_RSA] = "rsa",
Soby Mathew2fd70f62017-08-31 11:50:29 +010092 [KEY_ALG_RSA_1_5] = "rsa_1_5",
Juan Castilloa2224ab2015-06-30 13:36:57 +010093#ifndef OPENSSL_NO_EC
Juan Castillof9f39c32015-06-01 16:34:23 +010094 [KEY_ALG_ECDSA] = "ecdsa"
Juan Castilloa2224ab2015-06-30 13:36:57 +010095#endif /* OPENSSL_NO_EC */
Juan Castillof9f39c32015-06-01 16:34:23 +010096};
97
Juan Castillo1218dd52015-07-03 16:23:16 +010098static void print_help(const char *cmd, const struct option *long_opt)
Juan Castillo11abdcd2014-10-21 11:30:42 +010099{
Juan Castillo212f7382015-12-15 16:37:57 +0000100 int rem, i = 0;
101 const struct option *opt;
102 char line[HELP_OPT_MAX_LEN];
103 char *p;
104
105 assert(cmd != NULL);
106 assert(long_opt != NULL);
107
Juan Castillo11abdcd2014-10-21 11:30:42 +0100108 printf("\n\n");
109 printf("The certificate generation tool loads the binary images and\n"
110 "optionally the RSA keys, and outputs the key and content\n"
111 "certificates properly signed to implement the chain of trust.\n"
112 "If keys are provided, they must be in PEM format.\n"
113 "Certificates are generated in DER format.\n");
114 printf("\n");
Juan Castillo212f7382015-12-15 16:37:57 +0000115 printf("Usage:\n");
116 printf("\t%s [OPTIONS]\n\n", cmd);
117
118 printf("Available options:\n");
Juan Castillo212f7382015-12-15 16:37:57 +0000119 opt = long_opt;
120 while (opt->name) {
121 p = line;
122 rem = HELP_OPT_MAX_LEN;
123 if (isalpha(opt->val)) {
124 /* Short format */
125 sprintf(p, "-%c,", (char)opt->val);
126 p += 3;
127 rem -= 3;
128 }
129 snprintf(p, rem, "--%s %s", opt->name,
130 (opt->has_arg == required_argument) ? "<arg>" : "");
131 printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i));
132 opt++;
133 i++;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100134 }
135 printf("\n");
Juan Castillo11abdcd2014-10-21 11:30:42 +0100136
137 exit(0);
138}
139
Juan Castillof9f39c32015-06-01 16:34:23 +0100140static int get_key_alg(const char *key_alg_str)
141{
142 int i;
143
144 for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) {
145 if (0 == strcmp(key_alg_str, key_algs_str[i])) {
146 return i;
147 }
148 }
149
150 return -1;
151}
152
Juan Castillo11abdcd2014-10-21 11:30:42 +0100153static void check_cmd_params(void)
154{
Juan Castillo86958fd2015-07-08 12:11:38 +0100155 cert_t *cert;
156 ext_t *ext;
157 key_t *key;
158 int i, j;
159
Juan Castillof9f39c32015-06-01 16:34:23 +0100160 /* Only save new keys */
161 if (save_keys && !new_keys) {
162 ERROR("Only new keys can be saved to disk\n");
163 exit(1);
164 }
165
Juan Castillo86958fd2015-07-08 12:11:38 +0100166 /* Check that all required options have been specified in the
167 * command line */
168 for (i = 0; i < num_certs; i++) {
169 cert = &certs[i];
170 if (cert->fn == NULL) {
171 /* Certificate not requested. Skip to the next one */
172 continue;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100173 }
174
Juan Castillo86958fd2015-07-08 12:11:38 +0100175 /* Check that all parameters required to create this certificate
176 * have been specified in the command line */
177 for (j = 0; j < cert->num_ext; j++) {
178 ext = &extensions[cert->ext[j]];
179 switch (ext->type) {
Juan Castillo43529982016-01-22 11:05:24 +0000180 case EXT_TYPE_NVCOUNTER:
181 /* Counter value must be specified */
182 if ((!ext->optional) && (ext->arg == NULL)) {
183 ERROR("Value for '%s' not specified\n",
184 ext->ln);
185 exit(1);
186 }
187 break;
Juan Castillo86958fd2015-07-08 12:11:38 +0100188 case EXT_TYPE_PKEY:
189 /* Key filename must be specified */
Juan Castillo43529982016-01-22 11:05:24 +0000190 key = &keys[ext->attr.key];
Juan Castillo86958fd2015-07-08 12:11:38 +0100191 if (!new_keys && key->fn == NULL) {
192 ERROR("Key '%s' required by '%s' not "
193 "specified\n", key->desc,
194 cert->cn);
195 exit(1);
196 }
197 break;
198 case EXT_TYPE_HASH:
Yatharth Kochar5752b592015-08-21 15:30:55 +0100199 /*
200 * Binary image must be specified
201 * unless it is explicitly made optional.
202 */
Juan Castillo43529982016-01-22 11:05:24 +0000203 if ((!ext->optional) && (ext->arg == NULL)) {
Juan Castillo86958fd2015-07-08 12:11:38 +0100204 ERROR("Image for '%s' not specified\n",
205 ext->ln);
206 exit(1);
207 }
208 break;
209 default:
Juan Castillo43529982016-01-22 11:05:24 +0000210 ERROR("Unknown extension type '%d' in '%s'\n",
211 ext->type, ext->ln);
Juan Castillo86958fd2015-07-08 12:11:38 +0100212 exit(1);
213 break;
214 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100215 }
216 }
217}
218
Juan Castillo212f7382015-12-15 16:37:57 +0000219/* Common command line options */
220static const cmd_opt_t common_cmd_opt[] = {
221 {
222 { "help", no_argument, NULL, 'h' },
223 "Print this message and exit"
224 },
225 {
226 { "key-alg", required_argument, NULL, 'a' },
Soby Mathew2fd70f62017-08-31 11:50:29 +0100227 "Key algorithm: 'rsa' (default) - RSAPSS scheme as per \
228PKCS#1 v2.1, 'rsa_1_5' - RSA PKCS#1 v1.5, 'ecdsa'"
Juan Castillo212f7382015-12-15 16:37:57 +0000229 },
230 {
231 { "save-keys", no_argument, NULL, 'k' },
232 "Save key pairs into files. Filenames must be provided"
233 },
234 {
235 { "new-keys", no_argument, NULL, 'n' },
236 "Generate new key pairs if no key files are provided"
237 },
238 {
239 { "print-cert", no_argument, NULL, 'p' },
240 "Print the certificates in the standard output"
241 }
242};
243
Juan Castillo11abdcd2014-10-21 11:30:42 +0100244int main(int argc, char *argv[])
245{
Masahiro Yamada48cb5e52017-02-06 19:47:44 +0900246 STACK_OF(X509_EXTENSION) * sk;
247 X509_EXTENSION *cert_ext;
248 ext_t *ext;
249 key_t *key;
250 cert_t *cert;
251 FILE *file;
Juan Castillo43529982016-01-22 11:05:24 +0000252 int i, j, ext_nid, nvctr;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100253 int c, opt_idx = 0;
Juan Castillo1218dd52015-07-03 16:23:16 +0100254 const struct option *cmd_opt;
255 const char *cur_opt;
Juan Castillof9f39c32015-06-01 16:34:23 +0100256 unsigned int err_code;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100257 unsigned char md[SHA256_DIGEST_LENGTH];
Juan Castilloac402932015-03-05 14:30:00 +0000258 const EVP_MD *md_info;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100259
260 NOTICE("CoT Generation Tool: %s\n", build_msg);
261 NOTICE("Target platform: %s\n", platform_msg);
262
Juan Castillof9f39c32015-06-01 16:34:23 +0100263 /* Set default options */
264 key_alg = KEY_ALG_RSA;
265
Juan Castillo1218dd52015-07-03 16:23:16 +0100266 /* Add common command line options */
Juan Castillo212f7382015-12-15 16:37:57 +0000267 for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
268 cmd_opt_add(&common_cmd_opt[i]);
269 }
Juan Castillo1218dd52015-07-03 16:23:16 +0100270
271 /* Initialize the certificates */
272 if (cert_init() != 0) {
273 ERROR("Cannot initialize certificates\n");
274 exit(1);
275 }
276
277 /* Initialize the keys */
278 if (key_init() != 0) {
279 ERROR("Cannot initialize keys\n");
280 exit(1);
281 }
282
283 /* Initialize the new types and register OIDs for the extensions */
284 if (ext_init() != 0) {
285 ERROR("Cannot initialize TBB extensions\n");
286 exit(1);
287 }
288
289 /* Get the command line options populated during the initialization */
290 cmd_opt = cmd_opt_get_array();
291
Juan Castillo11abdcd2014-10-21 11:30:42 +0100292 while (1) {
293 /* getopt_long stores the option index here. */
Juan Castillo212f7382015-12-15 16:37:57 +0000294 c = getopt_long(argc, argv, "a:hknp", cmd_opt, &opt_idx);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100295
296 /* Detect the end of the options. */
297 if (c == -1) {
298 break;
299 }
300
301 switch (c) {
Juan Castillof9f39c32015-06-01 16:34:23 +0100302 case 'a':
303 key_alg = get_key_alg(optarg);
304 if (key_alg < 0) {
305 ERROR("Invalid key algorithm '%s'\n", optarg);
306 exit(1);
307 }
308 break;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100309 case 'h':
Juan Castillo1218dd52015-07-03 16:23:16 +0100310 print_help(argv[0], cmd_opt);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100311 break;
312 case 'k':
313 save_keys = 1;
314 break;
315 case 'n':
316 new_keys = 1;
317 break;
318 case 'p':
319 print_cert = 1;
320 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100321 case CMD_OPT_EXT:
322 cur_opt = cmd_opt_get_name(opt_idx);
323 ext = ext_get_by_opt(cur_opt);
Juan Castillo43529982016-01-22 11:05:24 +0000324 ext->arg = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100325 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100326 case CMD_OPT_KEY:
327 cur_opt = cmd_opt_get_name(opt_idx);
328 key = key_get_by_opt(cur_opt);
329 key->fn = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100330 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100331 case CMD_OPT_CERT:
332 cur_opt = cmd_opt_get_name(opt_idx);
333 cert = cert_get_by_opt(cur_opt);
334 cert->fn = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100335 break;
336 case '?':
337 default:
Juan Castillo212f7382015-12-15 16:37:57 +0000338 print_help(argv[0], cmd_opt);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100339 exit(1);
340 }
341 }
342
Juan Castillo11abdcd2014-10-21 11:30:42 +0100343 /* Check command line arguments */
344 check_cmd_params();
345
Juan Castilloac402932015-03-05 14:30:00 +0000346 /* Indicate SHA256 as image hash algorithm in the certificate
347 * extension */
348 md_info = EVP_sha256();
349
Juan Castillo11abdcd2014-10-21 11:30:42 +0100350 /* Load private keys from files (or generate new ones) */
Juan Castilloe6d30e92015-06-12 11:27:59 +0100351 for (i = 0 ; i < num_keys ; i++) {
Masahiro Yamadabccb1092017-02-06 21:15:01 +0900352 if (!key_new(&keys[i])) {
353 ERROR("Failed to allocate key container\n");
354 exit(1);
355 }
356
Juan Castillof9f39c32015-06-01 16:34:23 +0100357 /* First try to load the key from disk */
358 if (key_load(&keys[i], &err_code)) {
359 /* Key loaded successfully */
360 continue;
361 }
362
363 /* Key not loaded. Check the error code */
Masahiro Yamadabccb1092017-02-06 21:15:01 +0900364 if (err_code == KEY_ERR_LOAD) {
Juan Castillof9f39c32015-06-01 16:34:23 +0100365 /* File exists, but it does not contain a valid private
366 * key. Abort. */
367 ERROR("Error loading '%s'\n", keys[i].fn);
368 exit(1);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100369 }
Juan Castillof9f39c32015-06-01 16:34:23 +0100370
371 /* File does not exist, could not be opened or no filename was
372 * given */
373 if (new_keys) {
374 /* Try to create a new key */
375 NOTICE("Creating new key for '%s'\n", keys[i].desc);
376 if (!key_create(&keys[i], key_alg)) {
377 ERROR("Error creating key '%s'\n", keys[i].desc);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100378 exit(1);
379 }
Juan Castillof9f39c32015-06-01 16:34:23 +0100380 } else {
381 if (err_code == KEY_ERR_OPEN) {
382 ERROR("Error opening '%s'\n", keys[i].fn);
383 } else {
384 ERROR("Key '%s' not specified\n", keys[i].desc);
385 }
386 exit(1);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100387 }
388 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100389
Juan Castilloe6d30e92015-06-12 11:27:59 +0100390 /* Create the certificates */
391 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100392
Juan Castilloe6d30e92015-06-12 11:27:59 +0100393 cert = &certs[i];
Juan Castillo11abdcd2014-10-21 11:30:42 +0100394
Juan Castilloe6d30e92015-06-12 11:27:59 +0100395 /* Create a new stack of extensions. This stack will be used
396 * to create the certificate */
Juan Castillo11abdcd2014-10-21 11:30:42 +0100397 CHECK_NULL(sk, sk_X509_EXTENSION_new_null());
Juan Castillo11abdcd2014-10-21 11:30:42 +0100398
Juan Castilloe6d30e92015-06-12 11:27:59 +0100399 for (j = 0 ; j < cert->num_ext ; j++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100400
Juan Castilloe6d30e92015-06-12 11:27:59 +0100401 ext = &extensions[cert->ext[j]];
Juan Castillo11abdcd2014-10-21 11:30:42 +0100402
Juan Castilloe6d30e92015-06-12 11:27:59 +0100403 /* Get OpenSSL internal ID for this extension */
404 CHECK_OID(ext_nid, ext->oid);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100405
Juan Castilloe6d30e92015-06-12 11:27:59 +0100406 /*
407 * Three types of extensions are currently supported:
408 * - EXT_TYPE_NVCOUNTER
409 * - EXT_TYPE_HASH
410 * - EXT_TYPE_PKEY
411 */
412 switch (ext->type) {
413 case EXT_TYPE_NVCOUNTER:
Yatharth Kochar79f86402016-06-22 14:49:27 +0100414 if (ext->arg) {
415 nvctr = atoi(ext->arg);
416 CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid,
Juan Castillo43529982016-01-22 11:05:24 +0000417 EXT_CRIT, nvctr));
Yatharth Kochar79f86402016-06-22 14:49:27 +0100418 }
Juan Castilloe6d30e92015-06-12 11:27:59 +0100419 break;
420 case EXT_TYPE_HASH:
Juan Castillo43529982016-01-22 11:05:24 +0000421 if (ext->arg == NULL) {
Yatharth Kochar5752b592015-08-21 15:30:55 +0100422 if (ext->optional) {
423 /* Include a hash filled with zeros */
424 memset(md, 0x0, SHA256_DIGEST_LENGTH);
425 } else {
426 /* Do not include this hash in the certificate */
427 break;
428 }
429 } else {
430 /* Calculate the hash of the file */
Juan Castillo43529982016-01-22 11:05:24 +0000431 if (!sha_file(ext->arg, md)) {
Yatharth Kochar5752b592015-08-21 15:30:55 +0100432 ERROR("Cannot calculate hash of %s\n",
Juan Castillo43529982016-01-22 11:05:24 +0000433 ext->arg);
Yatharth Kochar5752b592015-08-21 15:30:55 +0100434 exit(1);
435 }
Juan Castilloe6d30e92015-06-12 11:27:59 +0100436 }
437 CHECK_NULL(cert_ext, ext_new_hash(ext_nid,
438 EXT_CRIT, md_info, md,
439 SHA256_DIGEST_LENGTH));
440 break;
441 case EXT_TYPE_PKEY:
442 CHECK_NULL(cert_ext, ext_new_key(ext_nid,
Juan Castillo43529982016-01-22 11:05:24 +0000443 EXT_CRIT, keys[ext->attr.key].key));
Juan Castilloe6d30e92015-06-12 11:27:59 +0100444 break;
445 default:
Juan Castillo43529982016-01-22 11:05:24 +0000446 ERROR("Unknown extension type '%d' in %s\n",
447 ext->type, cert->cn);
Juan Castilloe6d30e92015-06-12 11:27:59 +0100448 exit(1);
449 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100450
Juan Castilloe6d30e92015-06-12 11:27:59 +0100451 /* Push the extension into the stack */
452 sk_X509_EXTENSION_push(sk, cert_ext);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100453 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100454
Soby Mathew2fd70f62017-08-31 11:50:29 +0100455 /* Create certificate. Signed with corresponding key */
456 if (cert->fn && !cert_new(key_alg, cert, VAL_DAYS, 0, sk)) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100457 ERROR("Cannot create %s\n", cert->cn);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100458 exit(1);
459 }
460
461 sk_X509_EXTENSION_free(sk);
462 }
463
Juan Castillo11abdcd2014-10-21 11:30:42 +0100464
465 /* Print the certificates */
466 if (print_cert) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100467 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100468 if (!certs[i].x) {
469 continue;
470 }
471 printf("\n\n=====================================\n\n");
472 X509_print_fp(stdout, certs[i].x);
473 }
474 }
475
476 /* Save created certificates to files */
Juan Castilloe6d30e92015-06-12 11:27:59 +0100477 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100478 if (certs[i].x && certs[i].fn) {
479 file = fopen(certs[i].fn, "w");
480 if (file != NULL) {
481 i2d_X509_fp(file, certs[i].x);
482 fclose(file);
483 } else {
484 ERROR("Cannot create file %s\n", certs[i].fn);
485 }
486 }
487 }
488
489 /* Save keys */
490 if (save_keys) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100491 for (i = 0 ; i < num_keys ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100492 if (!key_store(&keys[i])) {
493 ERROR("Cannot save %s\n", keys[i].desc);
494 }
495 }
496 }
497
Juan Castillo11abdcd2014-10-21 11:30:42 +0100498#ifndef OPENSSL_NO_ENGINE
499 ENGINE_cleanup();
500#endif
501 CRYPTO_cleanup_all_ex_data();
502
503 return 0;
504}