xref: /rk3399_ARM-atf/tools/cert_create/src/key.c (revision 616b3ce27d9a8a83a189a16ff6a05698bc6df3c8)
16f971622SJuan Castillo /*
2cf2dd17dSJuan Pablo Conde  * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
36f971622SJuan Castillo  *
482cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
56f971622SJuan Castillo  */
66f971622SJuan Castillo 
76f971622SJuan Castillo #include <getopt.h>
86f971622SJuan Castillo #include <stdio.h>
96f971622SJuan Castillo #include <stdlib.h>
106f971622SJuan Castillo #include <string.h>
116f971622SJuan Castillo 
12*616b3ce2SRobin van der Gracht /* Suppress OpenSSL engine deprecation warnings */
13*616b3ce2SRobin van der Gracht #define OPENSSL_SUPPRESS_DEPRECATED
14*616b3ce2SRobin van der Gracht 
156f971622SJuan Castillo #include <openssl/conf.h>
16*616b3ce2SRobin van der Gracht #include <openssl/engine.h>
176f971622SJuan Castillo #include <openssl/evp.h>
186f971622SJuan Castillo #include <openssl/pem.h>
196f971622SJuan Castillo 
206f971622SJuan Castillo #include "cert.h"
21ad2c1a9aSJuan Castillo #include "cmd_opt.h"
226f971622SJuan Castillo #include "debug.h"
236f971622SJuan Castillo #include "key.h"
246f971622SJuan Castillo #include "sha.h"
256f971622SJuan Castillo 
266f971622SJuan Castillo #define MAX_FILENAME_LEN		1024
276f971622SJuan Castillo 
28b94bf967SPankaj Gupta key_t *keys;
29b94bf967SPankaj Gupta unsigned int num_keys;
30b94bf967SPankaj Gupta 
31cf2dd17dSJuan Pablo Conde #if !USING_OPENSSL3
326f971622SJuan Castillo /*
33ccbf890eSJuan Castillo  * Create a new key container
346f971622SJuan Castillo  */
35762f1ebeSMasahiro Yamada int key_new(key_t *key)
36ccbf890eSJuan Castillo {
37ccbf890eSJuan Castillo 	/* Create key pair container */
38ccbf890eSJuan Castillo 	key->key = EVP_PKEY_new();
39ccbf890eSJuan Castillo 	if (key->key == NULL) {
40ccbf890eSJuan Castillo 		return 0;
41ccbf890eSJuan Castillo 	}
42ccbf890eSJuan Castillo 
43ccbf890eSJuan Castillo 	return 1;
44ccbf890eSJuan Castillo }
45cf2dd17dSJuan Pablo Conde #endif
46ccbf890eSJuan Castillo 
47dfe0f4c2SJustin Chadwell static int key_create_rsa(key_t *key, int key_bits)
486f971622SJuan Castillo {
49cf2dd17dSJuan Pablo Conde #if USING_OPENSSL3
509bc52d33SJuan Pablo Conde 	EVP_PKEY *rsa = EVP_RSA_gen(key_bits);
51ccbf890eSJuan Castillo 	if (rsa == NULL) {
52742c4e14SMichalis Pappas 		printf("Cannot generate RSA key\n");
53ed2a76eaSJuan Castillo 		return 0;
54ed2a76eaSJuan Castillo 	}
559bc52d33SJuan Pablo Conde 	key->key = rsa;
569bc52d33SJuan Pablo Conde 	return 1;
57cf2dd17dSJuan Pablo Conde #else
58cf2dd17dSJuan Pablo Conde 	BIGNUM *e;
59cf2dd17dSJuan Pablo Conde 	RSA *rsa = NULL;
60cf2dd17dSJuan Pablo Conde 
61cf2dd17dSJuan Pablo Conde 	e = BN_new();
62cf2dd17dSJuan Pablo Conde 	if (e == NULL) {
63cf2dd17dSJuan Pablo Conde 		printf("Cannot create RSA exponent\n");
64cf2dd17dSJuan Pablo Conde 		return 0;
65cf2dd17dSJuan Pablo Conde 	}
66cf2dd17dSJuan Pablo Conde 
67cf2dd17dSJuan Pablo Conde 	if (!BN_set_word(e, RSA_F4)) {
68cf2dd17dSJuan Pablo Conde 		printf("Cannot assign RSA exponent\n");
69cf2dd17dSJuan Pablo Conde 		goto err2;
70cf2dd17dSJuan Pablo Conde 	}
71cf2dd17dSJuan Pablo Conde 
72cf2dd17dSJuan Pablo Conde 	rsa = RSA_new();
73cf2dd17dSJuan Pablo Conde 	if (rsa == NULL) {
74cf2dd17dSJuan Pablo Conde 		printf("Cannot create RSA key\n");
75cf2dd17dSJuan Pablo Conde 		goto err2;
76cf2dd17dSJuan Pablo Conde 	}
77cf2dd17dSJuan Pablo Conde 
78cf2dd17dSJuan Pablo Conde 	if (!RSA_generate_key_ex(rsa, key_bits, e, NULL)) {
79cf2dd17dSJuan Pablo Conde 		printf("Cannot generate RSA key\n");
80cf2dd17dSJuan Pablo Conde 		goto err;
81cf2dd17dSJuan Pablo Conde 	}
82cf2dd17dSJuan Pablo Conde 
83cf2dd17dSJuan Pablo Conde 	if (!EVP_PKEY_assign_RSA(key->key, rsa)) {
84cf2dd17dSJuan Pablo Conde 		printf("Cannot assign RSA key\n");
85cf2dd17dSJuan Pablo Conde 		goto err;
86cf2dd17dSJuan Pablo Conde 	}
87cf2dd17dSJuan Pablo Conde 
88cf2dd17dSJuan Pablo Conde 	BN_free(e);
89cf2dd17dSJuan Pablo Conde 	return 1;
90cf2dd17dSJuan Pablo Conde 
91cf2dd17dSJuan Pablo Conde err:
92cf2dd17dSJuan Pablo Conde 	RSA_free(rsa);
93cf2dd17dSJuan Pablo Conde err2:
94cf2dd17dSJuan Pablo Conde 	BN_free(e);
95cf2dd17dSJuan Pablo Conde 	return 0;
96cf2dd17dSJuan Pablo Conde #endif
979bc52d33SJuan Pablo Conde }
98ed2a76eaSJuan Castillo 
99ed2a76eaSJuan Castillo #ifndef OPENSSL_NO_EC
100cf2dd17dSJuan Pablo Conde #if USING_OPENSSL3
101e78ba69eSLionel Debieve static int key_create_ecdsa(key_t *key, int key_bits, const char *curve)
102e78ba69eSLionel Debieve {
103e78ba69eSLionel Debieve 	EVP_PKEY *ec = EVP_EC_gen(curve);
104ccbf890eSJuan Castillo 	if (ec == NULL) {
105ccbf890eSJuan Castillo 		printf("Cannot generate EC key\n");
106ed2a76eaSJuan Castillo 		return 0;
107ed2a76eaSJuan Castillo 	}
108e78ba69eSLionel Debieve 
1099bc52d33SJuan Pablo Conde 	key->key = ec;
1109bc52d33SJuan Pablo Conde 	return 1;
111e78ba69eSLionel Debieve }
112e78ba69eSLionel Debieve 
113e78ba69eSLionel Debieve static int key_create_ecdsa_nist(key_t *key, int key_bits)
114e78ba69eSLionel Debieve {
115e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, "prime256v1");
116e78ba69eSLionel Debieve }
117e78ba69eSLionel Debieve 
118e78ba69eSLionel Debieve static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits)
119e78ba69eSLionel Debieve {
120e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, "brainpoolP256r1");
121e78ba69eSLionel Debieve }
122e78ba69eSLionel Debieve 
123e78ba69eSLionel Debieve static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits)
124e78ba69eSLionel Debieve {
125e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, "brainpoolP256t1");
126e78ba69eSLionel Debieve }
127cf2dd17dSJuan Pablo Conde #else
128e78ba69eSLionel Debieve static int key_create_ecdsa(key_t *key, int key_bits, const int curve_id)
129e78ba69eSLionel Debieve {
130cf2dd17dSJuan Pablo Conde 	EC_KEY *ec;
131cf2dd17dSJuan Pablo Conde 
132e78ba69eSLionel Debieve 	ec = EC_KEY_new_by_curve_name(curve_id);
133cf2dd17dSJuan Pablo Conde 	if (ec == NULL) {
134cf2dd17dSJuan Pablo Conde 		printf("Cannot create EC key\n");
135cf2dd17dSJuan Pablo Conde 		return 0;
136cf2dd17dSJuan Pablo Conde 	}
137cf2dd17dSJuan Pablo Conde 	if (!EC_KEY_generate_key(ec)) {
138cf2dd17dSJuan Pablo Conde 		printf("Cannot generate EC key\n");
139cf2dd17dSJuan Pablo Conde 		goto err;
140cf2dd17dSJuan Pablo Conde 	}
141cf2dd17dSJuan Pablo Conde 	EC_KEY_set_flags(ec, EC_PKEY_NO_PARAMETERS);
142cf2dd17dSJuan Pablo Conde 	EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
143cf2dd17dSJuan Pablo Conde 	if (!EVP_PKEY_assign_EC_KEY(key->key, ec)) {
144cf2dd17dSJuan Pablo Conde 		printf("Cannot assign EC key\n");
145cf2dd17dSJuan Pablo Conde 		goto err;
146cf2dd17dSJuan Pablo Conde 	}
147cf2dd17dSJuan Pablo Conde 
148cf2dd17dSJuan Pablo Conde 	return 1;
149cf2dd17dSJuan Pablo Conde 
150cf2dd17dSJuan Pablo Conde err:
151cf2dd17dSJuan Pablo Conde 	EC_KEY_free(ec);
152cf2dd17dSJuan Pablo Conde 	return 0;
1539bc52d33SJuan Pablo Conde }
154e78ba69eSLionel Debieve 
155e78ba69eSLionel Debieve static int key_create_ecdsa_nist(key_t *key, int key_bits)
156e78ba69eSLionel Debieve {
157e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, NID_X9_62_prime256v1);
158e78ba69eSLionel Debieve }
159e78ba69eSLionel Debieve 
160e78ba69eSLionel Debieve static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits)
161e78ba69eSLionel Debieve {
162e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, NID_brainpoolP256r1);
163e78ba69eSLionel Debieve }
164e78ba69eSLionel Debieve 
165e78ba69eSLionel Debieve static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits)
166e78ba69eSLionel Debieve {
167e78ba69eSLionel Debieve 	return key_create_ecdsa(key, key_bits, NID_brainpoolP256t1);
168e78ba69eSLionel Debieve }
169e78ba69eSLionel Debieve #endif /* USING_OPENSSL3 */
170ed2a76eaSJuan Castillo #endif /* OPENSSL_NO_EC */
171ed2a76eaSJuan Castillo 
172dfe0f4c2SJustin Chadwell typedef int (*key_create_fn_t)(key_t *key, int key_bits);
173ed2a76eaSJuan Castillo static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = {
174e78ba69eSLionel Debieve 	[KEY_ALG_RSA] = key_create_rsa,
175ed2a76eaSJuan Castillo #ifndef OPENSSL_NO_EC
176e78ba69eSLionel Debieve 	[KEY_ALG_ECDSA_NIST] = key_create_ecdsa_nist,
177e78ba69eSLionel Debieve 	[KEY_ALG_ECDSA_BRAINPOOL_R] = key_create_ecdsa_brainpool_r,
178e78ba69eSLionel Debieve 	[KEY_ALG_ECDSA_BRAINPOOL_T] = key_create_ecdsa_brainpool_t,
179ed2a76eaSJuan Castillo #endif /* OPENSSL_NO_EC */
180ed2a76eaSJuan Castillo };
181ed2a76eaSJuan Castillo 
182dfe0f4c2SJustin Chadwell int key_create(key_t *key, int type, int key_bits)
183ed2a76eaSJuan Castillo {
184ed2a76eaSJuan Castillo 	if (type >= KEY_ALG_MAX_NUM) {
185ed2a76eaSJuan Castillo 		printf("Invalid key type\n");
186ed2a76eaSJuan Castillo 		return 0;
187ed2a76eaSJuan Castillo 	}
188ed2a76eaSJuan Castillo 
189ed2a76eaSJuan Castillo 	if (key_create_fn[type]) {
190dfe0f4c2SJustin Chadwell 		return key_create_fn[type](key, key_bits);
191ed2a76eaSJuan Castillo 	}
192ccbf890eSJuan Castillo 
1936f971622SJuan Castillo 	return 0;
1946f971622SJuan Castillo }
1956f971622SJuan Castillo 
196*616b3ce2SRobin van der Gracht static EVP_PKEY *key_load_pkcs11(const char *uri)
197*616b3ce2SRobin van der Gracht {
198*616b3ce2SRobin van der Gracht 	char *key_pass;
199*616b3ce2SRobin van der Gracht 	EVP_PKEY *pkey;
200*616b3ce2SRobin van der Gracht 	ENGINE *e;
201*616b3ce2SRobin van der Gracht 
202*616b3ce2SRobin van der Gracht 	ENGINE_load_builtin_engines();
203*616b3ce2SRobin van der Gracht 	e = ENGINE_by_id("pkcs11");
204*616b3ce2SRobin van der Gracht 	if (!e) {
205*616b3ce2SRobin van der Gracht 		fprintf(stderr, "Cannot Load PKCS#11 ENGINE\n");
206*616b3ce2SRobin van der Gracht 		return NULL;
207*616b3ce2SRobin van der Gracht 	}
208*616b3ce2SRobin van der Gracht 
209*616b3ce2SRobin van der Gracht 	if (!ENGINE_init(e)) {
210*616b3ce2SRobin van der Gracht 		fprintf(stderr, "Cannot ENGINE_init\n");
211*616b3ce2SRobin van der Gracht 		goto err;
212*616b3ce2SRobin van der Gracht 	}
213*616b3ce2SRobin van der Gracht 
214*616b3ce2SRobin van der Gracht 	key_pass = getenv("PKCS11_PIN");
215*616b3ce2SRobin van der Gracht 	if (key_pass) {
216*616b3ce2SRobin van der Gracht 		if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) {
217*616b3ce2SRobin van der Gracht 			fprintf(stderr, "Cannot Set PKCS#11 PIN\n");
218*616b3ce2SRobin van der Gracht 			goto err;
219*616b3ce2SRobin van der Gracht 		}
220*616b3ce2SRobin van der Gracht 	}
221*616b3ce2SRobin van der Gracht 
222*616b3ce2SRobin van der Gracht 	pkey = ENGINE_load_private_key(e, uri, NULL, NULL);
223*616b3ce2SRobin van der Gracht 	if (pkey)
224*616b3ce2SRobin van der Gracht 		return pkey;
225*616b3ce2SRobin van der Gracht err:
226*616b3ce2SRobin van der Gracht 	ENGINE_free(e);
227*616b3ce2SRobin van der Gracht 	return NULL;
228*616b3ce2SRobin van der Gracht 
229*616b3ce2SRobin van der Gracht }
230*616b3ce2SRobin van der Gracht 
231ccbf890eSJuan Castillo int key_load(key_t *key, unsigned int *err_code)
2326f971622SJuan Castillo {
233c893c733SMasahiro Yamada 	FILE *fp;
2346f971622SJuan Castillo 
2356f971622SJuan Castillo 	if (key->fn) {
236*616b3ce2SRobin van der Gracht 		if (!strncmp(key->fn, "pkcs11:", 7)) {
237*616b3ce2SRobin van der Gracht 			/* Load key through pkcs11 */
238*616b3ce2SRobin van der Gracht 			key->key = key_load_pkcs11(key->fn);
239*616b3ce2SRobin van der Gracht 		} else {
2406f971622SJuan Castillo 			/* Load key from file */
2416f971622SJuan Castillo 			fp = fopen(key->fn, "r");
2426f971622SJuan Castillo 			if (fp) {
243ea6f8452SRobin van der Gracht 				key->key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
2446f971622SJuan Castillo 				fclose(fp);
245*616b3ce2SRobin van der Gracht 			} else {
246*616b3ce2SRobin van der Gracht 				WARN("Cannot open file %s\n", key->fn);
247*616b3ce2SRobin van der Gracht 				*err_code = KEY_ERR_OPEN;
248*616b3ce2SRobin van der Gracht 			}
249*616b3ce2SRobin van der Gracht 		}
250ea6f8452SRobin van der Gracht 		if (key->key) {
251ccbf890eSJuan Castillo 			*err_code = KEY_ERR_NONE;
2526f971622SJuan Castillo 			return 1;
2536f971622SJuan Castillo 		} else {
254ccbf890eSJuan Castillo 			ERROR("Cannot load key from %s\n", key->fn);
255ccbf890eSJuan Castillo 			*err_code = KEY_ERR_LOAD;
2566f971622SJuan Castillo 		}
2576f971622SJuan Castillo 	} else {
258*616b3ce2SRobin van der Gracht 		VERBOSE("Key not specified\n");
259ccbf890eSJuan Castillo 		*err_code = KEY_ERR_FILENAME;
2606f971622SJuan Castillo 	}
2616f971622SJuan Castillo 
2626f971622SJuan Castillo 	return 0;
2636f971622SJuan Castillo }
2646f971622SJuan Castillo 
2656f971622SJuan Castillo int key_store(key_t *key)
2666f971622SJuan Castillo {
267c893c733SMasahiro Yamada 	FILE *fp;
2686f971622SJuan Castillo 
2696f971622SJuan Castillo 	if (key->fn) {
270*616b3ce2SRobin van der Gracht 		if (!strncmp(key->fn, "pkcs11:", 7)) {
271*616b3ce2SRobin van der Gracht 			ERROR("PKCS11 URI provided instead of a file");
272*616b3ce2SRobin van der Gracht 			return 0;
273*616b3ce2SRobin van der Gracht 		}
2746f971622SJuan Castillo 		fp = fopen(key->fn, "w");
2756f971622SJuan Castillo 		if (fp) {
2766f971622SJuan Castillo 			PEM_write_PrivateKey(fp, key->key,
2776f971622SJuan Castillo 					NULL, NULL, 0, NULL, NULL);
2786f971622SJuan Castillo 			fclose(fp);
2796f971622SJuan Castillo 			return 1;
2806f971622SJuan Castillo 		} else {
2816f971622SJuan Castillo 			ERROR("Cannot create file %s\n", key->fn);
2826f971622SJuan Castillo 		}
2836f971622SJuan Castillo 	} else {
2846f971622SJuan Castillo 		ERROR("Key filename not specified\n");
2856f971622SJuan Castillo 	}
2866f971622SJuan Castillo 
2876f971622SJuan Castillo 	return 0;
2886f971622SJuan Castillo }
289ad2c1a9aSJuan Castillo 
290ad2c1a9aSJuan Castillo int key_init(void)
291ad2c1a9aSJuan Castillo {
292159807e2SJuan Castillo 	cmd_opt_t cmd_opt;
293ad2c1a9aSJuan Castillo 	key_t *key;
294ad2c1a9aSJuan Castillo 	unsigned int i;
295ad2c1a9aSJuan Castillo 
296b94bf967SPankaj Gupta 	keys = malloc((num_def_keys * sizeof(def_keys[0]))
297b94bf967SPankaj Gupta #ifdef PDEF_KEYS
298b94bf967SPankaj Gupta 		      + (num_pdef_keys * sizeof(pdef_keys[0]))
299b94bf967SPankaj Gupta #endif
300b94bf967SPankaj Gupta 		      );
301b94bf967SPankaj Gupta 
302b94bf967SPankaj Gupta 	if (keys == NULL) {
303b94bf967SPankaj Gupta 		ERROR("%s:%d Failed to allocate memory.\n", __func__, __LINE__);
304b94bf967SPankaj Gupta 		return 1;
305b94bf967SPankaj Gupta 	}
306b94bf967SPankaj Gupta 
307b94bf967SPankaj Gupta 	memcpy(&keys[0], &def_keys[0], (num_def_keys * sizeof(def_keys[0])));
308b94bf967SPankaj Gupta #ifdef PDEF_KEYS
309b94bf967SPankaj Gupta 	memcpy(&keys[num_def_keys], &pdef_keys[0],
310b94bf967SPankaj Gupta 		(num_pdef_keys * sizeof(pdef_keys[0])));
311b94bf967SPankaj Gupta 
312b94bf967SPankaj Gupta 	num_keys = num_def_keys + num_pdef_keys;
313b94bf967SPankaj Gupta #else
314b94bf967SPankaj Gupta 	num_keys = num_def_keys;
315b94bf967SPankaj Gupta #endif
316b94bf967SPankaj Gupta 		   ;
317b94bf967SPankaj Gupta 
318ad2c1a9aSJuan Castillo 	for (i = 0; i < num_keys; i++) {
319ad2c1a9aSJuan Castillo 		key = &keys[i];
320ad2c1a9aSJuan Castillo 		if (key->opt != NULL) {
321159807e2SJuan Castillo 			cmd_opt.long_opt.name = key->opt;
322159807e2SJuan Castillo 			cmd_opt.long_opt.has_arg = required_argument;
323159807e2SJuan Castillo 			cmd_opt.long_opt.flag = NULL;
324159807e2SJuan Castillo 			cmd_opt.long_opt.val = CMD_OPT_KEY;
325159807e2SJuan Castillo 			cmd_opt.help_msg = key->help_msg;
326159807e2SJuan Castillo 			cmd_opt_add(&cmd_opt);
327ad2c1a9aSJuan Castillo 		}
328ad2c1a9aSJuan Castillo 	}
329ad2c1a9aSJuan Castillo 
330c893c733SMasahiro Yamada 	return 0;
331ad2c1a9aSJuan Castillo }
332ad2c1a9aSJuan Castillo 
333ad2c1a9aSJuan Castillo key_t *key_get_by_opt(const char *opt)
334ad2c1a9aSJuan Castillo {
335c893c733SMasahiro Yamada 	key_t *key;
336ad2c1a9aSJuan Castillo 	unsigned int i;
337ad2c1a9aSJuan Castillo 
338ad2c1a9aSJuan Castillo 	/* Sequential search. This is not a performance concern since the number
339ad2c1a9aSJuan Castillo 	 * of keys is bounded and the code runs on a host machine */
340ad2c1a9aSJuan Castillo 	for (i = 0; i < num_keys; i++) {
341ad2c1a9aSJuan Castillo 		key = &keys[i];
342ad2c1a9aSJuan Castillo 		if (0 == strcmp(key->opt, opt)) {
343ad2c1a9aSJuan Castillo 			return key;
344ad2c1a9aSJuan Castillo 		}
345ad2c1a9aSJuan Castillo 	}
346ad2c1a9aSJuan Castillo 
347ad2c1a9aSJuan Castillo 	return NULL;
348ad2c1a9aSJuan Castillo }
349cf2dd17dSJuan Pablo Conde 
350cf2dd17dSJuan Pablo Conde void key_cleanup(void)
351cf2dd17dSJuan Pablo Conde {
352cf2dd17dSJuan Pablo Conde 	unsigned int i;
353cf2dd17dSJuan Pablo Conde 
354cf2dd17dSJuan Pablo Conde 	for (i = 0; i < num_keys; i++) {
355cf2dd17dSJuan Pablo Conde 		EVP_PKEY_free(keys[i].key);
356cf2dd17dSJuan Pablo Conde 		if (keys[i].fn != NULL) {
357cf2dd17dSJuan Pablo Conde 			void *ptr = keys[i].fn;
358cf2dd17dSJuan Pablo Conde 
359cf2dd17dSJuan Pablo Conde 			free(ptr);
360cf2dd17dSJuan Pablo Conde 			keys[i].fn = NULL;
361cf2dd17dSJuan Pablo Conde 		}
362cf2dd17dSJuan Pablo Conde 	}
363cf2dd17dSJuan Pablo Conde 	free(keys);
364cf2dd17dSJuan Pablo Conde }
365cf2dd17dSJuan Pablo Conde 
366