xref: /optee_os/lib/libmbedtls/mbedtls/library/x509write_crt.c (revision a1d5c81f8834a9d2c6f4372cce2e59e70e709121)
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/asn1write.h"
38 #include "mbedtls/error.h"
39 #include "mbedtls/oid.h"
40 #include "mbedtls/platform_util.h"
41 #include "mbedtls/sha1.h"
42 
43 #include <string.h>
44 
45 #if defined(MBEDTLS_PEM_WRITE_C)
46 #include "mbedtls/pem.h"
47 #endif /* MBEDTLS_PEM_WRITE_C */
48 
49 void mbedtls_x509write_crt_init( mbedtls_x509write_cert *ctx )
50 {
51     memset( ctx, 0, sizeof( mbedtls_x509write_cert ) );
52 
53     mbedtls_mpi_init( &ctx->serial );
54     ctx->version = MBEDTLS_X509_CRT_VERSION_3;
55 }
56 
57 void mbedtls_x509write_crt_free( mbedtls_x509write_cert *ctx )
58 {
59     mbedtls_mpi_free( &ctx->serial );
60 
61     mbedtls_asn1_free_named_data_list( &ctx->subject );
62     mbedtls_asn1_free_named_data_list( &ctx->issuer );
63     mbedtls_asn1_free_named_data_list( &ctx->extensions );
64 
65     mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x509write_cert ) );
66 }
67 
68 void mbedtls_x509write_crt_set_version( mbedtls_x509write_cert *ctx,
69                                         int version )
70 {
71     ctx->version = version;
72 }
73 
74 void mbedtls_x509write_crt_set_md_alg( mbedtls_x509write_cert *ctx,
75                                        mbedtls_md_type_t md_alg )
76 {
77     ctx->md_alg = md_alg;
78 }
79 
80 void mbedtls_x509write_crt_set_subject_key( mbedtls_x509write_cert *ctx,
81                                             mbedtls_pk_context *key )
82 {
83     ctx->subject_key = key;
84 }
85 
86 void mbedtls_x509write_crt_set_issuer_key( mbedtls_x509write_cert *ctx,
87                                            mbedtls_pk_context *key )
88 {
89     ctx->issuer_key = key;
90 }
91 
92 int mbedtls_x509write_crt_set_subject_name( mbedtls_x509write_cert *ctx,
93                                             const char *subject_name )
94 {
95     return mbedtls_x509_string_to_names( &ctx->subject, subject_name );
96 }
97 
98 int mbedtls_x509write_crt_set_issuer_name( mbedtls_x509write_cert *ctx,
99                                            const char *issuer_name )
100 {
101     return mbedtls_x509_string_to_names( &ctx->issuer, issuer_name );
102 }
103 
104 int mbedtls_x509write_crt_set_serial( mbedtls_x509write_cert *ctx,
105                                       const mbedtls_mpi *serial )
106 {
107     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
108 
109     if( ( ret = mbedtls_mpi_copy( &ctx->serial, serial ) ) != 0 )
110         return( ret );
111 
112     return( 0 );
113 }
114 
115 int mbedtls_x509write_crt_set_validity( mbedtls_x509write_cert *ctx,
116                                         const char *not_before,
117                                         const char *not_after )
118 {
119     if( strlen( not_before ) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 ||
120         strlen( not_after )  != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 )
121     {
122         return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
123     }
124     strncpy( ctx->not_before, not_before, MBEDTLS_X509_RFC5280_UTC_TIME_LEN );
125     strncpy( ctx->not_after , not_after , MBEDTLS_X509_RFC5280_UTC_TIME_LEN );
126     ctx->not_before[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z';
127     ctx->not_after[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z';
128 
129     return( 0 );
130 }
131 
132 int mbedtls_x509write_crt_set_extension( mbedtls_x509write_cert *ctx,
133                                  const char *oid, size_t oid_len,
134                                  int critical,
135                                  const unsigned char *val, size_t val_len )
136 {
137     return( mbedtls_x509_set_extension( &ctx->extensions, oid, oid_len,
138                                         critical, val, val_len ) );
139 }
140 
141 int mbedtls_x509write_crt_set_basic_constraints( mbedtls_x509write_cert *ctx,
142                                                  int is_ca, int max_pathlen )
143 {
144     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
145     unsigned char buf[9];
146     unsigned char *c = buf + sizeof(buf);
147     size_t len = 0;
148 
149     memset( buf, 0, sizeof(buf) );
150 
151     if( is_ca && max_pathlen > 127 )
152         return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
153 
154     if( is_ca )
155     {
156         if( max_pathlen >= 0 )
157         {
158             MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf,
159                                                                max_pathlen ) );
160         }
161         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_bool( &c, buf, 1 ) );
162     }
163 
164     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
165     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf,
166                                                 MBEDTLS_ASN1_CONSTRUCTED |
167                                                 MBEDTLS_ASN1_SEQUENCE ) );
168 
169     return(
170         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 = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
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,
185                 mbedtls_pk_write_pubkey( &c, buf, ctx->subject_key ) );
186 
187     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
188                             buf + sizeof( buf ) - 20 );
189     if( ret != 0 )
190         return( ret );
191     c = buf + sizeof( buf ) - 20;
192     len = 20;
193 
194     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
195     MBEDTLS_ASN1_CHK_ADD( len,
196             mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_OCTET_STRING ) );
197 
198     return mbedtls_x509write_crt_set_extension( ctx,
199                  MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER,
200                  MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER ),
201                  0, buf + sizeof(buf) - len, len );
202 }
203 
204 int mbedtls_x509write_crt_set_authority_key_identifier( mbedtls_x509write_cert *ctx )
205 {
206     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
207     unsigned char buf[MBEDTLS_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */
208     unsigned char *c = buf + sizeof( buf );
209     size_t len = 0;
210 
211     memset( buf, 0, sizeof(buf) );
212     MBEDTLS_ASN1_CHK_ADD( len,
213                           mbedtls_pk_write_pubkey( &c, buf, ctx->issuer_key ) );
214 
215     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
216                             buf + sizeof( buf ) - 20 );
217     if( ret != 0 )
218         return( ret );
219     c = buf + sizeof( buf ) - 20;
220     len = 20;
221 
222     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
223     MBEDTLS_ASN1_CHK_ADD( len,
224         mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0 ) );
225 
226     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
227     MBEDTLS_ASN1_CHK_ADD( len,
228                           mbedtls_asn1_write_tag( &c, buf,
229                                                   MBEDTLS_ASN1_CONSTRUCTED |
230                                                   MBEDTLS_ASN1_SEQUENCE ) );
231 
232     return mbedtls_x509write_crt_set_extension(
233         ctx, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER,
234         MBEDTLS_OID_SIZE( MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER ),
235         0, buf + sizeof( buf ) - len, len );
236 }
237 #endif /* MBEDTLS_SHA1_C */
238 
239 int mbedtls_x509write_crt_set_key_usage( mbedtls_x509write_cert *ctx,
240                                          unsigned int key_usage )
241 {
242     unsigned char buf[5], ku[2];
243     unsigned char *c;
244     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
245     const unsigned int allowed_bits = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
246         MBEDTLS_X509_KU_NON_REPUDIATION   |
247         MBEDTLS_X509_KU_KEY_ENCIPHERMENT  |
248         MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
249         MBEDTLS_X509_KU_KEY_AGREEMENT     |
250         MBEDTLS_X509_KU_KEY_CERT_SIGN     |
251         MBEDTLS_X509_KU_CRL_SIGN          |
252         MBEDTLS_X509_KU_ENCIPHER_ONLY     |
253         MBEDTLS_X509_KU_DECIPHER_ONLY;
254 
255     /* Check that nothing other than the allowed flags is set */
256     if( ( key_usage & ~allowed_bits ) != 0 )
257         return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE );
258 
259     c = buf + 5;
260     ku[0] = (unsigned char)( key_usage      );
261     ku[1] = (unsigned char)( key_usage >> 8 );
262     ret = mbedtls_asn1_write_named_bitstring( &c, buf, ku, 9 );
263 
264     if( ret < 0 )
265         return( ret );
266     else if( ret < 3 || ret > 5 )
267         return( MBEDTLS_ERR_X509_INVALID_FORMAT );
268 
269     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_KEY_USAGE,
270                                    MBEDTLS_OID_SIZE( MBEDTLS_OID_KEY_USAGE ),
271                                    1, c, (size_t)ret );
272     if( ret != 0 )
273         return( ret );
274 
275     return( 0 );
276 }
277 
278 int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
279                                     unsigned char ns_cert_type )
280 {
281     unsigned char buf[4];
282     unsigned char *c;
283     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
284 
285     c = buf + 4;
286 
287     ret = mbedtls_asn1_write_named_bitstring( &c, buf, &ns_cert_type, 8 );
288     if( ret < 3 || ret > 4 )
289         return( ret );
290 
291     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_NS_CERT_TYPE,
292                                    MBEDTLS_OID_SIZE( MBEDTLS_OID_NS_CERT_TYPE ),
293                                    0, c, (size_t)ret );
294     if( ret != 0 )
295         return( ret );
296 
297     return( 0 );
298 }
299 
300 static int x509_write_time( unsigned char **p, unsigned char *start,
301                             const char *t, size_t size )
302 {
303     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
304     size_t len = 0;
305 
306     /*
307      * write MBEDTLS_ASN1_UTC_TIME if year < 2050 (2 bytes shorter)
308      */
309     if( t[0] == '2' && t[1] == '0' && t[2] < '5' )
310     {
311         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( p, start,
312                                              (const unsigned char *) t + 2,
313                                              size - 2 ) );
314         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
315         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start,
316                                              MBEDTLS_ASN1_UTC_TIME ) );
317     }
318     else
319     {
320         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( p, start,
321                                                   (const unsigned char *) t,
322                                                   size ) );
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,
325                                              MBEDTLS_ASN1_GENERALIZED_TIME ) );
326     }
327 
328     return( (int) len );
329 }
330 
331 int mbedtls_x509write_crt_der( mbedtls_x509write_cert *ctx,
332                                unsigned char *buf, size_t size,
333                                int (*f_rng)(void *, unsigned char *, size_t),
334                                void *p_rng )
335 {
336     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
337     const char *sig_oid;
338     size_t sig_oid_len = 0;
339     unsigned char *c, *c2;
340     unsigned char hash[64];
341     unsigned char sig[MBEDTLS_PK_SIGNATURE_MAX_SIZE];
342     size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len;
343     size_t len = 0;
344     mbedtls_pk_type_t pk_alg;
345 
346     /*
347      * Prepare data to be signed at the end of the target buffer
348      */
349     c = buf + size;
350 
351     /* Signature algorithm needed in TBS, and later for actual signature */
352 
353     /* There's no direct way of extracting a signature algorithm
354      * (represented as an element of mbedtls_pk_type_t) from a PK instance. */
355     if( mbedtls_pk_can_do( ctx->issuer_key, MBEDTLS_PK_RSA ) )
356         pk_alg = MBEDTLS_PK_RSA;
357     else if( mbedtls_pk_can_do( ctx->issuer_key, MBEDTLS_PK_ECDSA ) )
358         pk_alg = MBEDTLS_PK_ECDSA;
359     else
360         return( MBEDTLS_ERR_X509_INVALID_ALG );
361 
362     if( ( ret = mbedtls_oid_get_oid_by_sig_alg( pk_alg, ctx->md_alg,
363                                           &sig_oid, &sig_oid_len ) ) != 0 )
364     {
365         return( ret );
366     }
367 
368     /*
369      *  Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
370      */
371 
372     /* Only for v3 */
373     if( ctx->version == MBEDTLS_X509_CRT_VERSION_3 )
374     {
375         MBEDTLS_ASN1_CHK_ADD( len,
376                               mbedtls_x509_write_extensions( &c,
377                                                       buf, ctx->extensions ) );
378         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
379         MBEDTLS_ASN1_CHK_ADD( len,
380                               mbedtls_asn1_write_tag( &c, buf,
381                                                       MBEDTLS_ASN1_CONSTRUCTED |
382                                                       MBEDTLS_ASN1_SEQUENCE ) );
383         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
384         MBEDTLS_ASN1_CHK_ADD( len,
385                               mbedtls_asn1_write_tag( &c, buf,
386                                                MBEDTLS_ASN1_CONTEXT_SPECIFIC |
387                                                MBEDTLS_ASN1_CONSTRUCTED | 3 ) );
388     }
389 
390     /*
391      *  SubjectPublicKeyInfo
392      */
393     MBEDTLS_ASN1_CHK_ADD( pub_len,
394                           mbedtls_pk_write_pubkey_der( ctx->subject_key,
395                                                        buf, c - buf ) );
396     c -= pub_len;
397     len += pub_len;
398 
399     /*
400      *  Subject  ::=  Name
401      */
402     MBEDTLS_ASN1_CHK_ADD( len,
403                           mbedtls_x509_write_names( &c, buf,
404                                                     ctx->subject ) );
405 
406     /*
407      *  Validity ::= SEQUENCE {
408      *       notBefore      Time,
409      *       notAfter       Time }
410      */
411     sub_len = 0;
412 
413     MBEDTLS_ASN1_CHK_ADD( sub_len,
414                           x509_write_time( &c, buf, ctx->not_after,
415                                         MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
416 
417     MBEDTLS_ASN1_CHK_ADD( sub_len,
418                           x509_write_time( &c, buf, ctx->not_before,
419                                         MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
420 
421     len += sub_len;
422     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, sub_len ) );
423     MBEDTLS_ASN1_CHK_ADD( len,
424                           mbedtls_asn1_write_tag( &c, buf,
425                                                   MBEDTLS_ASN1_CONSTRUCTED |
426                                                   MBEDTLS_ASN1_SEQUENCE ) );
427 
428     /*
429      *  Issuer  ::=  Name
430      */
431     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, buf,
432                                                          ctx->issuer ) );
433 
434     /*
435      *  Signature   ::=  AlgorithmIdentifier
436      */
437     MBEDTLS_ASN1_CHK_ADD( len,
438                           mbedtls_asn1_write_algorithm_identifier( &c, buf,
439                                               sig_oid, strlen( sig_oid ), 0 ) );
440 
441     /*
442      *  Serial   ::=  INTEGER
443      */
444     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, buf,
445                                                        &ctx->serial ) );
446 
447     /*
448      *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
449      */
450 
451     /* Can be omitted for v1 */
452     if( ctx->version != MBEDTLS_X509_CRT_VERSION_1 )
453     {
454         sub_len = 0;
455         MBEDTLS_ASN1_CHK_ADD( sub_len,
456                               mbedtls_asn1_write_int( &c, buf, ctx->version ) );
457         len += sub_len;
458         MBEDTLS_ASN1_CHK_ADD( len,
459                               mbedtls_asn1_write_len( &c, buf, sub_len ) );
460         MBEDTLS_ASN1_CHK_ADD( len,
461                               mbedtls_asn1_write_tag( &c, buf,
462                                                MBEDTLS_ASN1_CONTEXT_SPECIFIC |
463                                                MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
464     }
465 
466     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
467     MBEDTLS_ASN1_CHK_ADD( len,
468                 mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
469                                                      MBEDTLS_ASN1_SEQUENCE ) );
470 
471     /*
472      * Make signature
473      */
474 
475     /* Compute hash of CRT. */
476     if( ( ret = mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c,
477                             len, hash ) ) != 0 )
478     {
479         return( ret );
480     }
481 
482     if( ( ret = mbedtls_pk_sign( ctx->issuer_key, ctx->md_alg,
483                                  hash, 0, sig, &sig_len,
484                                  f_rng, p_rng ) ) != 0 )
485     {
486         return( ret );
487     }
488 
489     /* Move CRT to the front of the buffer to have space
490      * for the signature. */
491     memmove( buf, c, len );
492     c = buf + len;
493 
494     /* Add signature at the end of the buffer,
495      * making sure that it doesn't underflow
496      * into the CRT buffer. */
497     c2 = buf + size;
498     MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len, mbedtls_x509_write_sig( &c2, c,
499                                         sig_oid, sig_oid_len, sig, sig_len ) );
500 
501     /*
502      * Memory layout after this step:
503      *
504      * buf       c=buf+len                c2            buf+size
505      * [CRT0,...,CRTn, UNUSED, ..., UNUSED, SIG0, ..., SIGm]
506      */
507 
508     /* Move raw CRT to just before the signature. */
509     c = c2 - len;
510     memmove( c, buf, len );
511 
512     len += sig_and_oid_len;
513     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
514     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf,
515                                                  MBEDTLS_ASN1_CONSTRUCTED |
516                                                  MBEDTLS_ASN1_SEQUENCE ) );
517 
518     return( (int) len );
519 }
520 
521 #define PEM_BEGIN_CRT           "-----BEGIN CERTIFICATE-----\n"
522 #define PEM_END_CRT             "-----END CERTIFICATE-----\n"
523 
524 #if defined(MBEDTLS_PEM_WRITE_C)
525 int mbedtls_x509write_crt_pem( mbedtls_x509write_cert *crt,
526                                unsigned char *buf, size_t size,
527                                int (*f_rng)(void *, unsigned char *, size_t),
528                                void *p_rng )
529 {
530     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
531     size_t olen;
532 
533     if( ( ret = mbedtls_x509write_crt_der( crt, buf, size,
534                                    f_rng, p_rng ) ) < 0 )
535     {
536         return( ret );
537     }
538 
539     if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_CRT, PEM_END_CRT,
540                                           buf + size - ret, ret,
541                                           buf, size, &olen ) ) != 0 )
542     {
543         return( ret );
544     }
545 
546     return( 0 );
547 }
548 #endif /* MBEDTLS_PEM_WRITE_C */
549 
550 #endif /* MBEDTLS_X509_CRT_WRITE_C */
551