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