]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Privacy Enhanced Mail (PEM) decoding | |
3 | * | |
4 | * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved | |
5 | * SPDX-License-Identifier: GPL-2.0 | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | * | |
21 | * This file is part of mbed TLS (https://tls.mbed.org) | |
22 | */ | |
23 | ||
24 | #if !defined(MBEDTLS_CONFIG_FILE) | |
25 | #include "mbedtls/config.h" | |
26 | #else | |
27 | #include MBEDTLS_CONFIG_FILE | |
28 | #endif | |
29 | ||
30 | #if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) | |
31 | ||
32 | #include "mbedtls/pem.h" | |
33 | #include "mbedtls/base64.h" | |
34 | #include "mbedtls/des.h" | |
35 | #include "mbedtls/aes.h" | |
36 | #include "mbedtls/md5.h" | |
37 | #include "mbedtls/cipher.h" | |
38 | #include "mbedtls/platform_util.h" | |
39 | ||
40 | #include <string.h> | |
41 | ||
42 | #if defined(MBEDTLS_PLATFORM_C) | |
43 | #include "mbedtls/platform.h" | |
44 | #else | |
45 | #include <stdlib.h> | |
46 | #define mbedtls_calloc calloc | |
47 | #define mbedtls_free free | |
48 | #endif | |
49 | ||
50 | #if defined(MBEDTLS_PEM_PARSE_C) | |
51 | void mbedtls_pem_init( mbedtls_pem_context *ctx ) | |
52 | { | |
53 | memset( ctx, 0, sizeof( mbedtls_pem_context ) ); | |
54 | } | |
55 | ||
56 | #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) && \ | |
57 | ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) ) | |
58 | /* | |
59 | * Read a 16-byte hex string and convert it to binary | |
60 | */ | |
61 | static int pem_get_iv( const unsigned char *s, unsigned char *iv, | |
62 | size_t iv_len ) | |
63 | { | |
64 | size_t i, j, k; | |
65 | ||
66 | memset( iv, 0, iv_len ); | |
67 | ||
68 | for( i = 0; i < iv_len * 2; i++, s++ ) | |
69 | { | |
70 | if( *s >= '0' && *s <= '9' ) j = *s - '0'; else | |
71 | if( *s >= 'A' && *s <= 'F' ) j = *s - '7'; else | |
72 | if( *s >= 'a' && *s <= 'f' ) j = *s - 'W'; else | |
73 | return( MBEDTLS_ERR_PEM_INVALID_ENC_IV ); | |
74 | ||
75 | k = ( ( i & 1 ) != 0 ) ? j : j << 4; | |
76 | ||
77 | iv[i >> 1] = (unsigned char)( iv[i >> 1] | k ); | |
78 | } | |
79 | ||
80 | return( 0 ); | |
81 | } | |
82 | ||
83 | static int pem_pbkdf1( unsigned char *key, size_t keylen, | |
84 | unsigned char *iv, | |
85 | const unsigned char *pwd, size_t pwdlen ) | |
86 | { | |
87 | mbedtls_md5_context md5_ctx; | |
88 | unsigned char md5sum[16]; | |
89 | size_t use_len; | |
90 | int ret; | |
91 | ||
92 | mbedtls_md5_init( &md5_ctx ); | |
93 | ||
94 | /* | |
95 | * key[ 0..15] = MD5(pwd || IV) | |
96 | */ | |
97 | if( ( ret = mbedtls_md5_starts_ret( &md5_ctx ) ) != 0 ) | |
98 | goto exit; | |
99 | if( ( ret = mbedtls_md5_update_ret( &md5_ctx, pwd, pwdlen ) ) != 0 ) | |
100 | goto exit; | |
101 | if( ( ret = mbedtls_md5_update_ret( &md5_ctx, iv, 8 ) ) != 0 ) | |
102 | goto exit; | |
103 | if( ( ret = mbedtls_md5_finish_ret( &md5_ctx, md5sum ) ) != 0 ) | |
104 | goto exit; | |
105 | ||
106 | if( keylen <= 16 ) | |
107 | { | |
108 | memcpy( key, md5sum, keylen ); | |
109 | goto exit; | |
110 | } | |
111 | ||
112 | memcpy( key, md5sum, 16 ); | |
113 | ||
114 | /* | |
115 | * key[16..23] = MD5(key[ 0..15] || pwd || IV]) | |
116 | */ | |
117 | if( ( ret = mbedtls_md5_starts_ret( &md5_ctx ) ) != 0 ) | |
118 | goto exit; | |
119 | if( ( ret = mbedtls_md5_update_ret( &md5_ctx, md5sum, 16 ) ) != 0 ) | |
120 | goto exit; | |
121 | if( ( ret = mbedtls_md5_update_ret( &md5_ctx, pwd, pwdlen ) ) != 0 ) | |
122 | goto exit; | |
123 | if( ( ret = mbedtls_md5_update_ret( &md5_ctx, iv, 8 ) ) != 0 ) | |
124 | goto exit; | |
125 | if( ( ret = mbedtls_md5_finish_ret( &md5_ctx, md5sum ) ) != 0 ) | |
126 | goto exit; | |
127 | ||
128 | use_len = 16; | |
129 | if( keylen < 32 ) | |
130 | use_len = keylen - 16; | |
131 | ||
132 | memcpy( key + 16, md5sum, use_len ); | |
133 | ||
134 | exit: | |
135 | mbedtls_md5_free( &md5_ctx ); | |
136 | mbedtls_platform_zeroize( md5sum, 16 ); | |
137 | ||
138 | return( ret ); | |
139 | } | |
140 | ||
141 | #if defined(MBEDTLS_DES_C) | |
142 | /* | |
143 | * Decrypt with DES-CBC, using PBKDF1 for key derivation | |
144 | */ | |
145 | static int pem_des_decrypt( unsigned char des_iv[8], | |
146 | unsigned char *buf, size_t buflen, | |
147 | const unsigned char *pwd, size_t pwdlen ) | |
148 | { | |
149 | mbedtls_des_context des_ctx; | |
150 | unsigned char des_key[8]; | |
151 | int ret; | |
152 | ||
153 | mbedtls_des_init( &des_ctx ); | |
154 | ||
155 | if( ( ret = pem_pbkdf1( des_key, 8, des_iv, pwd, pwdlen ) ) != 0 ) | |
156 | goto exit; | |
157 | ||
158 | if( ( ret = mbedtls_des_setkey_dec( &des_ctx, des_key ) ) != 0 ) | |
159 | goto exit; | |
160 | ret = mbedtls_des_crypt_cbc( &des_ctx, MBEDTLS_DES_DECRYPT, buflen, | |
161 | des_iv, buf, buf ); | |
162 | ||
163 | exit: | |
164 | mbedtls_des_free( &des_ctx ); | |
165 | mbedtls_platform_zeroize( des_key, 8 ); | |
166 | ||
167 | return( ret ); | |
168 | } | |
169 | ||
170 | /* | |
171 | * Decrypt with 3DES-CBC, using PBKDF1 for key derivation | |
172 | */ | |
173 | static int pem_des3_decrypt( unsigned char des3_iv[8], | |
174 | unsigned char *buf, size_t buflen, | |
175 | const unsigned char *pwd, size_t pwdlen ) | |
176 | { | |
177 | mbedtls_des3_context des3_ctx; | |
178 | unsigned char des3_key[24]; | |
179 | int ret; | |
180 | ||
181 | mbedtls_des3_init( &des3_ctx ); | |
182 | ||
183 | if( ( ret = pem_pbkdf1( des3_key, 24, des3_iv, pwd, pwdlen ) ) != 0 ) | |
184 | goto exit; | |
185 | ||
186 | if( ( ret = mbedtls_des3_set3key_dec( &des3_ctx, des3_key ) ) != 0 ) | |
187 | goto exit; | |
188 | ret = mbedtls_des3_crypt_cbc( &des3_ctx, MBEDTLS_DES_DECRYPT, buflen, | |
189 | des3_iv, buf, buf ); | |
190 | ||
191 | exit: | |
192 | mbedtls_des3_free( &des3_ctx ); | |
193 | mbedtls_platform_zeroize( des3_key, 24 ); | |
194 | ||
195 | return( ret ); | |
196 | } | |
197 | #endif /* MBEDTLS_DES_C */ | |
198 | ||
199 | #if defined(MBEDTLS_AES_C) | |
200 | /* | |
201 | * Decrypt with AES-XXX-CBC, using PBKDF1 for key derivation | |
202 | */ | |
203 | static int pem_aes_decrypt( unsigned char aes_iv[16], unsigned int keylen, | |
204 | unsigned char *buf, size_t buflen, | |
205 | const unsigned char *pwd, size_t pwdlen ) | |
206 | { | |
207 | mbedtls_aes_context aes_ctx; | |
208 | unsigned char aes_key[32]; | |
209 | int ret; | |
210 | ||
211 | mbedtls_aes_init( &aes_ctx ); | |
212 | ||
213 | if( ( ret = pem_pbkdf1( aes_key, keylen, aes_iv, pwd, pwdlen ) ) != 0 ) | |
214 | goto exit; | |
215 | ||
216 | if( ( ret = mbedtls_aes_setkey_dec( &aes_ctx, aes_key, keylen * 8 ) ) != 0 ) | |
217 | goto exit; | |
218 | ret = mbedtls_aes_crypt_cbc( &aes_ctx, MBEDTLS_AES_DECRYPT, buflen, | |
219 | aes_iv, buf, buf ); | |
220 | ||
221 | exit: | |
222 | mbedtls_aes_free( &aes_ctx ); | |
223 | mbedtls_platform_zeroize( aes_key, keylen ); | |
224 | ||
225 | return( ret ); | |
226 | } | |
227 | #endif /* MBEDTLS_AES_C */ | |
228 | ||
229 | #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC && | |
230 | ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ | |
231 | ||
232 | int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const char *footer, | |
233 | const unsigned char *data, const unsigned char *pwd, | |
234 | size_t pwdlen, size_t *use_len ) | |
235 | { | |
236 | int ret, enc; | |
237 | size_t len; | |
238 | unsigned char *buf; | |
239 | const unsigned char *s1, *s2, *end; | |
240 | #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) && \ | |
241 | ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) ) | |
242 | unsigned char pem_iv[16]; | |
243 | mbedtls_cipher_type_t enc_alg = MBEDTLS_CIPHER_NONE; | |
244 | #else | |
245 | ((void) pwd); | |
246 | ((void) pwdlen); | |
247 | #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC && | |
248 | ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ | |
249 | ||
250 | if( ctx == NULL ) | |
251 | return( MBEDTLS_ERR_PEM_BAD_INPUT_DATA ); | |
252 | ||
253 | s1 = (unsigned char *) strstr( (const char *) data, header ); | |
254 | ||
255 | if( s1 == NULL ) | |
256 | return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); | |
257 | ||
258 | s2 = (unsigned char *) strstr( (const char *) data, footer ); | |
259 | ||
260 | if( s2 == NULL || s2 <= s1 ) | |
261 | return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); | |
262 | ||
263 | s1 += strlen( header ); | |
264 | if( *s1 == ' ' ) s1++; | |
265 | if( *s1 == '\r' ) s1++; | |
266 | if( *s1 == '\n' ) s1++; | |
267 | else return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); | |
268 | ||
269 | end = s2; | |
270 | end += strlen( footer ); | |
271 | if( *end == ' ' ) end++; | |
272 | if( *end == '\r' ) end++; | |
273 | if( *end == '\n' ) end++; | |
274 | *use_len = end - data; | |
275 | ||
276 | enc = 0; | |
277 | ||
278 | if( s2 - s1 >= 22 && memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) | |
279 | { | |
280 | #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) && \ | |
281 | ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) ) | |
282 | enc++; | |
283 | ||
284 | s1 += 22; | |
285 | if( *s1 == '\r' ) s1++; | |
286 | if( *s1 == '\n' ) s1++; | |
287 | else return( MBEDTLS_ERR_PEM_INVALID_DATA ); | |
288 | ||
289 | ||
290 | #if defined(MBEDTLS_DES_C) | |
291 | if( s2 - s1 >= 23 && memcmp( s1, "DEK-Info: DES-EDE3-CBC,", 23 ) == 0 ) | |
292 | { | |
293 | enc_alg = MBEDTLS_CIPHER_DES_EDE3_CBC; | |
294 | ||
295 | s1 += 23; | |
296 | if( s2 - s1 < 16 || pem_get_iv( s1, pem_iv, 8 ) != 0 ) | |
297 | return( MBEDTLS_ERR_PEM_INVALID_ENC_IV ); | |
298 | ||
299 | s1 += 16; | |
300 | } | |
301 | else if( s2 - s1 >= 18 && memcmp( s1, "DEK-Info: DES-CBC,", 18 ) == 0 ) | |
302 | { | |
303 | enc_alg = MBEDTLS_CIPHER_DES_CBC; | |
304 | ||
305 | s1 += 18; | |
306 | if( s2 - s1 < 16 || pem_get_iv( s1, pem_iv, 8) != 0 ) | |
307 | return( MBEDTLS_ERR_PEM_INVALID_ENC_IV ); | |
308 | ||
309 | s1 += 16; | |
310 | } | |
311 | #endif /* MBEDTLS_DES_C */ | |
312 | ||
313 | #if defined(MBEDTLS_AES_C) | |
314 | if( s2 - s1 >= 14 && memcmp( s1, "DEK-Info: AES-", 14 ) == 0 ) | |
315 | { | |
316 | if( s2 - s1 < 22 ) | |
317 | return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG ); | |
318 | else if( memcmp( s1, "DEK-Info: AES-128-CBC,", 22 ) == 0 ) | |
319 | enc_alg = MBEDTLS_CIPHER_AES_128_CBC; | |
320 | else if( memcmp( s1, "DEK-Info: AES-192-CBC,", 22 ) == 0 ) | |
321 | enc_alg = MBEDTLS_CIPHER_AES_192_CBC; | |
322 | else if( memcmp( s1, "DEK-Info: AES-256-CBC,", 22 ) == 0 ) | |
323 | enc_alg = MBEDTLS_CIPHER_AES_256_CBC; | |
324 | else | |
325 | return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG ); | |
326 | ||
327 | s1 += 22; | |
328 | if( s2 - s1 < 32 || pem_get_iv( s1, pem_iv, 16 ) != 0 ) | |
329 | return( MBEDTLS_ERR_PEM_INVALID_ENC_IV ); | |
330 | ||
331 | s1 += 32; | |
332 | } | |
333 | #endif /* MBEDTLS_AES_C */ | |
334 | ||
335 | if( enc_alg == MBEDTLS_CIPHER_NONE ) | |
336 | return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG ); | |
337 | ||
338 | if( *s1 == '\r' ) s1++; | |
339 | if( *s1 == '\n' ) s1++; | |
340 | else return( MBEDTLS_ERR_PEM_INVALID_DATA ); | |
341 | #else | |
342 | return( MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE ); | |
343 | #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC && | |
344 | ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ | |
345 | } | |
346 | ||
347 | if( s1 >= s2 ) | |
348 | return( MBEDTLS_ERR_PEM_INVALID_DATA ); | |
349 | ||
350 | ret = mbedtls_base64_decode( NULL, 0, &len, s1, s2 - s1 ); | |
351 | ||
352 | if( ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER ) | |
353 | return( MBEDTLS_ERR_PEM_INVALID_DATA + ret ); | |
354 | ||
355 | if( ( buf = mbedtls_calloc( 1, len ) ) == NULL ) | |
356 | return( MBEDTLS_ERR_PEM_ALLOC_FAILED ); | |
357 | ||
358 | if( ( ret = mbedtls_base64_decode( buf, len, &len, s1, s2 - s1 ) ) != 0 ) | |
359 | { | |
360 | mbedtls_platform_zeroize( buf, len ); | |
361 | mbedtls_free( buf ); | |
362 | return( MBEDTLS_ERR_PEM_INVALID_DATA + ret ); | |
363 | } | |
364 | ||
365 | if( enc != 0 ) | |
366 | { | |
367 | #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) && \ | |
368 | ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) ) | |
369 | if( pwd == NULL ) | |
370 | { | |
371 | mbedtls_platform_zeroize( buf, len ); | |
372 | mbedtls_free( buf ); | |
373 | return( MBEDTLS_ERR_PEM_PASSWORD_REQUIRED ); | |
374 | } | |
375 | ||
376 | ret = 0; | |
377 | ||
378 | #if defined(MBEDTLS_DES_C) | |
379 | if( enc_alg == MBEDTLS_CIPHER_DES_EDE3_CBC ) | |
380 | ret = pem_des3_decrypt( pem_iv, buf, len, pwd, pwdlen ); | |
381 | else if( enc_alg == MBEDTLS_CIPHER_DES_CBC ) | |
382 | ret = pem_des_decrypt( pem_iv, buf, len, pwd, pwdlen ); | |
383 | #endif /* MBEDTLS_DES_C */ | |
384 | ||
385 | #if defined(MBEDTLS_AES_C) | |
386 | if( enc_alg == MBEDTLS_CIPHER_AES_128_CBC ) | |
387 | ret = pem_aes_decrypt( pem_iv, 16, buf, len, pwd, pwdlen ); | |
388 | else if( enc_alg == MBEDTLS_CIPHER_AES_192_CBC ) | |
389 | ret = pem_aes_decrypt( pem_iv, 24, buf, len, pwd, pwdlen ); | |
390 | else if( enc_alg == MBEDTLS_CIPHER_AES_256_CBC ) | |
391 | ret = pem_aes_decrypt( pem_iv, 32, buf, len, pwd, pwdlen ); | |
392 | #endif /* MBEDTLS_AES_C */ | |
393 | ||
394 | if( ret != 0 ) | |
395 | { | |
396 | mbedtls_free( buf ); | |
397 | return( ret ); | |
398 | } | |
399 | ||
400 | /* | |
401 | * The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3 | |
402 | * length bytes (allow 4 to be sure) in all known use cases. | |
403 | * | |
404 | * Use that as a heuristic to try to detect password mismatches. | |
405 | */ | |
406 | if( len <= 2 || buf[0] != 0x30 || buf[1] > 0x83 ) | |
407 | { | |
408 | mbedtls_platform_zeroize( buf, len ); | |
409 | mbedtls_free( buf ); | |
410 | return( MBEDTLS_ERR_PEM_PASSWORD_MISMATCH ); | |
411 | } | |
412 | #else | |
413 | mbedtls_platform_zeroize( buf, len ); | |
414 | mbedtls_free( buf ); | |
415 | return( MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE ); | |
416 | #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC && | |
417 | ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ | |
418 | } | |
419 | ||
420 | ctx->buf = buf; | |
421 | ctx->buflen = len; | |
422 | ||
423 | return( 0 ); | |
424 | } | |
425 | ||
426 | void mbedtls_pem_free( mbedtls_pem_context *ctx ) | |
427 | { | |
428 | if( ctx->buf != NULL ) | |
429 | mbedtls_platform_zeroize( ctx->buf, ctx->buflen ); | |
430 | mbedtls_free( ctx->buf ); | |
431 | mbedtls_free( ctx->info ); | |
432 | ||
433 | mbedtls_platform_zeroize( ctx, sizeof( mbedtls_pem_context ) ); | |
434 | } | |
435 | #endif /* MBEDTLS_PEM_PARSE_C */ | |
436 | ||
437 | #if defined(MBEDTLS_PEM_WRITE_C) | |
438 | int mbedtls_pem_write_buffer( const char *header, const char *footer, | |
439 | const unsigned char *der_data, size_t der_len, | |
440 | unsigned char *buf, size_t buf_len, size_t *olen ) | |
441 | { | |
442 | int ret; | |
443 | unsigned char *encode_buf = NULL, *c, *p = buf; | |
444 | size_t len = 0, use_len, add_len = 0; | |
445 | ||
446 | mbedtls_base64_encode( NULL, 0, &use_len, der_data, der_len ); | |
447 | add_len = strlen( header ) + strlen( footer ) + ( use_len / 64 ) + 1; | |
448 | ||
449 | if( use_len + add_len > buf_len ) | |
450 | { | |
451 | *olen = use_len + add_len; | |
452 | return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL ); | |
453 | } | |
454 | ||
455 | if( use_len != 0 && | |
456 | ( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) ) | |
457 | return( MBEDTLS_ERR_PEM_ALLOC_FAILED ); | |
458 | ||
459 | if( ( ret = mbedtls_base64_encode( encode_buf, use_len, &use_len, der_data, | |
460 | der_len ) ) != 0 ) | |
461 | { | |
462 | mbedtls_free( encode_buf ); | |
463 | return( ret ); | |
464 | } | |
465 | ||
466 | memcpy( p, header, strlen( header ) ); | |
467 | p += strlen( header ); | |
468 | c = encode_buf; | |
469 | ||
470 | while( use_len ) | |
471 | { | |
472 | len = ( use_len > 64 ) ? 64 : use_len; | |
473 | memcpy( p, c, len ); | |
474 | use_len -= len; | |
475 | p += len; | |
476 | c += len; | |
477 | *p++ = '\n'; | |
478 | } | |
479 | ||
480 | memcpy( p, footer, strlen( footer ) ); | |
481 | p += strlen( footer ); | |
482 | ||
483 | *p++ = '\0'; | |
484 | *olen = p - buf; | |
485 | ||
486 | mbedtls_free( encode_buf ); | |
487 | return( 0 ); | |
488 | } | |
489 | #endif /* MBEDTLS_PEM_WRITE_C */ | |
490 | #endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */ |