xref: /optee_os/lib/libmbedtls/mbedtls/library/x509write_crt.c (revision 5b25c76ac40f830867e3d60800120ffd7874e8dc)
1 // SPDX-License-Identifier: Apache-2.0
2 /*
3  *  X.509 certificate writing
4  *
5  *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
6  *
7  *  Licensed under the Apache License, Version 2.0 (the "License"); you may
8  *  not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *  http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  *
19  *  This file is part of mbed TLS (https://tls.mbed.org)
20  */
21 /*
22  * References:
23  * - certificates: RFC 5280, updated by RFC 6818
24  * - CSRs: PKCS#10 v1.7 aka RFC 2986
25  * - attributes: PKCS#9 v2.0 aka RFC 2985
26  */
27 
28 #if !defined(MBEDTLS_CONFIG_FILE)
29 #include "mbedtls/config.h"
30 #else
31 #include MBEDTLS_CONFIG_FILE
32 #endif
33 
34 #if defined(MBEDTLS_X509_CRT_WRITE_C)
35 
36 #include "mbedtls/x509_crt.h"
37 #include "mbedtls/oid.h"
38 #include "mbedtls/asn1write.h"
39 #include "mbedtls/sha1.h"
40 #include "mbedtls/platform_util.h"
41 
42 #include <string.h>
43 
44 #if defined(MBEDTLS_PEM_WRITE_C)
45 #include "mbedtls/pem.h"
46 #endif /* MBEDTLS_PEM_WRITE_C */
47 
48 /*
49  * For the currently used signature algorithms the buffer to store any signature
50  * must be at least of size MAX(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE)
51  */
52 #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
53 #define SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
54 #else
55 #define SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
56 #endif
57 
58 void mbedtls_x509write_crt_init( mbedtls_x509write_cert *ctx )
59 {
60     memset( ctx, 0, sizeof( mbedtls_x509write_cert ) );
61 
62     mbedtls_mpi_init( &ctx->serial );
63     ctx->version = MBEDTLS_X509_CRT_VERSION_3;
64 }
65 
66 void mbedtls_x509write_crt_free( mbedtls_x509write_cert *ctx )
67 {
68     mbedtls_mpi_free( &ctx->serial );
69 
70     mbedtls_asn1_free_named_data_list( &ctx->subject );
71     mbedtls_asn1_free_named_data_list( &ctx->issuer );
72     mbedtls_asn1_free_named_data_list( &ctx->extensions );
73 
74     mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x509write_cert ) );
75 }
76 
77 void mbedtls_x509write_crt_set_version( mbedtls_x509write_cert *ctx, int version )
78 {
79     ctx->version = version;
80 }
81 
82 void mbedtls_x509write_crt_set_md_alg( mbedtls_x509write_cert *ctx, mbedtls_md_type_t md_alg )
83 {
84     ctx->md_alg = md_alg;
85 }
86 
87 void mbedtls_x509write_crt_set_subject_key( mbedtls_x509write_cert *ctx, mbedtls_pk_context *key )
88 {
89     ctx->subject_key = key;
90 }
91 
92 void mbedtls_x509write_crt_set_issuer_key( mbedtls_x509write_cert *ctx, mbedtls_pk_context *key )
93 {
94     ctx->issuer_key = key;
95 }
96 
97 int mbedtls_x509write_crt_set_subject_name( mbedtls_x509write_cert *ctx,
98                                     const char *subject_name )
99 {
100     return mbedtls_x509_string_to_names( &ctx->subject, subject_name );
101 }
102 
103 int mbedtls_x509write_crt_set_issuer_name( mbedtls_x509write_cert *ctx,
104                                    const char *issuer_name )
105 {
106     return mbedtls_x509_string_to_names( &ctx->issuer, issuer_name );
107 }
108 
109 int mbedtls_x509write_crt_set_serial( mbedtls_x509write_cert *ctx, const mbedtls_mpi *serial )
110 {
111     int ret;
112 
113     if( ( ret = mbedtls_mpi_copy( &ctx->serial, serial ) ) != 0 )
114         return( ret );
115 
116     return( 0 );
117 }
118 
119 int mbedtls_x509write_crt_set_validity( mbedtls_x509write_cert *ctx, const char *not_before,
120                                 const char *not_after )
121 {
122     if( strlen( not_before ) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 ||
123         strlen( not_after )  != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 )
124     {
125         return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
126     }
127     strncpy( ctx->not_before, not_before, MBEDTLS_X509_RFC5280_UTC_TIME_LEN );
128     strncpy( ctx->not_after , not_after , MBEDTLS_X509_RFC5280_UTC_TIME_LEN );
129     ctx->not_before[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z';
130     ctx->not_after[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z';
131 
132     return( 0 );
133 }
134 
135 int mbedtls_x509write_crt_set_extension( mbedtls_x509write_cert *ctx,
136                                  const char *oid, size_t oid_len,
137                                  int critical,
138                                  const unsigned char *val, size_t val_len )
139 {
140     return mbedtls_x509_set_extension( &ctx->extensions, oid, oid_len,
141                                critical, val, val_len );
142 }
143 
144 int mbedtls_x509write_crt_set_basic_constraints( mbedtls_x509write_cert *ctx,
145                                          int is_ca, int max_pathlen )
146 {
147     int ret;
148     unsigned char buf[9];
149     unsigned char *c = buf + sizeof(buf);
150     size_t len = 0;
151 
152     memset( buf, 0, sizeof(buf) );
153 
154     if( is_ca && max_pathlen > 127 )
155         return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
156 
157     if( is_ca )
158     {
159         if( max_pathlen >= 0 )
160         {
161             MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, max_pathlen ) );
162         }
163         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_bool( &c, buf, 1 ) );
164     }
165 
166     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
167     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
168                                                 MBEDTLS_ASN1_SEQUENCE ) );
169 
170     return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_BASIC_CONSTRAINTS,
171                                         MBEDTLS_OID_SIZE( MBEDTLS_OID_BASIC_CONSTRAINTS ),
172                                         0, buf + sizeof(buf) - len, len );
173 }
174 
175 #if defined(MBEDTLS_SHA1_C)
176 int mbedtls_x509write_crt_set_subject_key_identifier( mbedtls_x509write_cert *ctx )
177 {
178     int ret;
179     unsigned char buf[MBEDTLS_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */
180     unsigned char *c = buf + sizeof(buf);
181     size_t len = 0;
182 
183     memset( buf, 0, sizeof(buf) );
184     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, ctx->subject_key ) );
185 
186     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
187                             buf + sizeof( buf ) - 20 );
188     if( ret != 0 )
189         return( ret );
190     c = buf + sizeof( buf ) - 20;
191     len = 20;
192 
193     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
194     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_OCTET_STRING ) );
195 
196     return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER,
197                                         MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER ),
198                                         0, buf + sizeof(buf) - len, len );
199 }
200 
201 int mbedtls_x509write_crt_set_authority_key_identifier( mbedtls_x509write_cert *ctx )
202 {
203     int ret;
204     unsigned char buf[MBEDTLS_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */
205     unsigned char *c = buf + sizeof( buf );
206     size_t len = 0;
207 
208     memset( buf, 0, sizeof(buf) );
209     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, ctx->issuer_key ) );
210 
211     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
212                             buf + sizeof( buf ) - 20 );
213     if( ret != 0 )
214         return( ret );
215     c = buf + sizeof( buf ) - 20;
216     len = 20;
217 
218     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
219     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0 ) );
220 
221     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
222     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
223                                                 MBEDTLS_ASN1_SEQUENCE ) );
224 
225     return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER,
226                                    MBEDTLS_OID_SIZE( MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER ),
227                                    0, buf + sizeof( buf ) - len, len );
228 }
229 #endif /* MBEDTLS_SHA1_C */
230 
231 static size_t crt_get_unused_bits_for_named_bitstring( unsigned char bitstring,
232                                                        size_t bit_offset )
233 {
234     size_t unused_bits;
235 
236      /* Count the unused bits removing trailing 0s */
237     for( unused_bits = bit_offset; unused_bits < 8; unused_bits++ )
238         if( ( ( bitstring >> unused_bits ) & 0x1 ) != 0 )
239             break;
240 
241      return( unused_bits );
242 }
243 
244 int mbedtls_x509write_crt_set_key_usage( mbedtls_x509write_cert *ctx,
245                                          unsigned int key_usage )
246 {
247     unsigned char buf[4], ku;
248     unsigned char *c;
249     int ret;
250     size_t unused_bits;
251     const unsigned int allowed_bits = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
252         MBEDTLS_X509_KU_NON_REPUDIATION   |
253         MBEDTLS_X509_KU_KEY_ENCIPHERMENT  |
254         MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
255         MBEDTLS_X509_KU_KEY_AGREEMENT     |
256         MBEDTLS_X509_KU_KEY_CERT_SIGN     |
257         MBEDTLS_X509_KU_CRL_SIGN;
258 
259     /* Check that nothing other than the allowed flags is set */
260     if( ( key_usage & ~allowed_bits ) != 0 )
261         return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE );
262 
263     c = buf + 4;
264     ku = (unsigned char)key_usage;
265     unused_bits = crt_get_unused_bits_for_named_bitstring( ku, 1 );
266     ret = mbedtls_asn1_write_bitstring( &c, buf, &ku, 8 - unused_bits );
267 
268     if( ret < 0 )
269         return( ret );
270     else if( ret < 3 || ret > 4 )
271         return( MBEDTLS_ERR_X509_INVALID_FORMAT );
272 
273     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_KEY_USAGE,
274                                        MBEDTLS_OID_SIZE( MBEDTLS_OID_KEY_USAGE ),
275                                        1, c, (size_t)ret );
276     if( ret != 0 )
277         return( ret );
278 
279     return( 0 );
280 }
281 
282 int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
283                                     unsigned char ns_cert_type )
284 {
285     unsigned char buf[4];
286     unsigned char *c;
287     size_t unused_bits;
288     int ret;
289 
290     c = buf + 4;
291 
292     unused_bits = crt_get_unused_bits_for_named_bitstring( ns_cert_type, 0 );
293     ret = mbedtls_asn1_write_bitstring( &c,
294                                         buf,
295                                         &ns_cert_type,
296                                         8 - unused_bits );
297     if( ret < 3 || ret > 4 )
298         return( ret );
299 
300     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_NS_CERT_TYPE,
301                                        MBEDTLS_OID_SIZE( MBEDTLS_OID_NS_CERT_TYPE ),
302                                        0, c, (size_t)ret );
303     if( ret != 0 )
304         return( ret );
305 
306     return( 0 );
307 }
308 
309 static int x509_write_time( unsigned char **p, unsigned char *start,
310                             const char *t, size_t size )
311 {
312     int ret;
313     size_t len = 0;
314 
315     /*
316      * write MBEDTLS_ASN1_UTC_TIME if year < 2050 (2 bytes shorter)
317      */
318     if( t[0] == '2' && t[1] == '0' && t[2] < '5' )
319     {
320         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( p, start,
321                                              (const unsigned char *) t + 2,
322                                              size - 2 ) );
323         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
324         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_UTC_TIME ) );
325     }
326     else
327     {
328         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( p, start,
329                                                   (const unsigned char *) t,
330                                                   size ) );
331         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
332         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_GENERALIZED_TIME ) );
333     }
334 
335     return( (int) len );
336 }
337 
338 int mbedtls_x509write_crt_der( mbedtls_x509write_cert *ctx, unsigned char *buf, size_t size,
339                        int (*f_rng)(void *, unsigned char *, size_t),
340                        void *p_rng )
341 {
342     int ret;
343     const char *sig_oid;
344     size_t sig_oid_len = 0;
345     unsigned char *c, *c2;
346     unsigned char hash[64];
347     unsigned char sig[SIGNATURE_MAX_SIZE];
348     unsigned char tmp_buf[2048];
349     size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len;
350     size_t len = 0;
351     mbedtls_pk_type_t pk_alg;
352 
353     /*
354      * Prepare data to be signed in tmp_buf
355      */
356     c = tmp_buf + sizeof( tmp_buf );
357 
358     /* Signature algorithm needed in TBS, and later for actual signature */
359 
360     /* There's no direct way of extracting a signature algorithm
361      * (represented as an element of mbedtls_pk_type_t) from a PK instance. */
362     if( mbedtls_pk_can_do( ctx->issuer_key, MBEDTLS_PK_RSA ) )
363         pk_alg = MBEDTLS_PK_RSA;
364     else if( mbedtls_pk_can_do( ctx->issuer_key, MBEDTLS_PK_ECDSA ) )
365         pk_alg = MBEDTLS_PK_ECDSA;
366     else
367         return( MBEDTLS_ERR_X509_INVALID_ALG );
368 
369     if( ( ret = mbedtls_oid_get_oid_by_sig_alg( pk_alg, ctx->md_alg,
370                                           &sig_oid, &sig_oid_len ) ) != 0 )
371     {
372         return( ret );
373     }
374 
375     /*
376      *  Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
377      */
378 
379     /* Only for v3 */
380     if( ctx->version == MBEDTLS_X509_CRT_VERSION_3 )
381     {
382         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_extensions( &c, tmp_buf, ctx->extensions ) );
383         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
384         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
385                                                            MBEDTLS_ASN1_SEQUENCE ) );
386         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
387         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC |
388                                                            MBEDTLS_ASN1_CONSTRUCTED | 3 ) );
389     }
390 
391     /*
392      *  SubjectPublicKeyInfo
393      */
394     MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_pk_write_pubkey_der( ctx->subject_key,
395                                                 tmp_buf, c - tmp_buf ) );
396     c -= pub_len;
397     len += pub_len;
398 
399     /*
400      *  Subject  ::=  Name
401      */
402     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, tmp_buf, ctx->subject ) );
403 
404     /*
405      *  Validity ::= SEQUENCE {
406      *       notBefore      Time,
407      *       notAfter       Time }
408      */
409     sub_len = 0;
410 
411     MBEDTLS_ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_after,
412                                             MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
413 
414     MBEDTLS_ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_before,
415                                             MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
416 
417     len += sub_len;
418     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, sub_len ) );
419     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
420                                                     MBEDTLS_ASN1_SEQUENCE ) );
421 
422     /*
423      *  Issuer  ::=  Name
424      */
425     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, tmp_buf, ctx->issuer ) );
426 
427     /*
428      *  Signature   ::=  AlgorithmIdentifier
429      */
430     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_algorithm_identifier( &c, tmp_buf,
431                        sig_oid, strlen( sig_oid ), 0 ) );
432 
433     /*
434      *  Serial   ::=  INTEGER
435      */
436     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, tmp_buf, &ctx->serial ) );
437 
438     /*
439      *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
440      */
441 
442     /* Can be omitted for v1 */
443     if( ctx->version != MBEDTLS_X509_CRT_VERSION_1 )
444     {
445         sub_len = 0;
446         MBEDTLS_ASN1_CHK_ADD( sub_len, mbedtls_asn1_write_int( &c, tmp_buf, ctx->version ) );
447         len += sub_len;
448         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, sub_len ) );
449         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC |
450                                                            MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
451     }
452 
453     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
454     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
455                                                        MBEDTLS_ASN1_SEQUENCE ) );
456 
457     /*
458      * Make signature
459      */
460     if( ( ret = mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c,
461                             len, hash ) ) != 0 )
462     {
463         return( ret );
464     }
465 
466     if( ( ret = mbedtls_pk_sign( ctx->issuer_key, ctx->md_alg, hash, 0, sig, &sig_len,
467                          f_rng, p_rng ) ) != 0 )
468     {
469         return( ret );
470     }
471 
472     /*
473      * Write data to output buffer
474      */
475     c2 = buf + size;
476     MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len, mbedtls_x509_write_sig( &c2, buf,
477                                         sig_oid, sig_oid_len, sig, sig_len ) );
478 
479     if( len > (size_t)( c2 - buf ) )
480         return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
481 
482     c2 -= len;
483     memcpy( c2, c, len );
484 
485     len += sig_and_oid_len;
486     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c2, buf, len ) );
487     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c2, buf, MBEDTLS_ASN1_CONSTRUCTED |
488                                                  MBEDTLS_ASN1_SEQUENCE ) );
489 
490     return( (int) len );
491 }
492 
493 #define PEM_BEGIN_CRT           "-----BEGIN CERTIFICATE-----\n"
494 #define PEM_END_CRT             "-----END CERTIFICATE-----\n"
495 
496 #if defined(MBEDTLS_PEM_WRITE_C)
497 int mbedtls_x509write_crt_pem( mbedtls_x509write_cert *crt, unsigned char *buf, size_t size,
498                        int (*f_rng)(void *, unsigned char *, size_t),
499                        void *p_rng )
500 {
501     int ret;
502     unsigned char output_buf[4096];
503     size_t olen = 0;
504 
505     if( ( ret = mbedtls_x509write_crt_der( crt, output_buf, sizeof(output_buf),
506                                    f_rng, p_rng ) ) < 0 )
507     {
508         return( ret );
509     }
510 
511     if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_CRT, PEM_END_CRT,
512                                   output_buf + sizeof(output_buf) - ret,
513                                   ret, buf, size, &olen ) ) != 0 )
514     {
515         return( ret );
516     }
517 
518     return( 0 );
519 }
520 #endif /* MBEDTLS_PEM_WRITE_C */
521 
522 #endif /* MBEDTLS_X509_CRT_WRITE_C */
523