• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Public Key layer for writing key files and structures
3  *
4  *  Copyright The Mbed TLS Contributors
5  *  SPDX-License-Identifier: Apache-2.0
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 
20 #include "common.h"
21 
22 #if defined(MBEDTLS_PK_WRITE_C)
23 
24 #include "mbedtls/pk.h"
25 #include "mbedtls/asn1write.h"
26 #include "mbedtls/oid.h"
27 #include "mbedtls/platform_util.h"
28 #include "mbedtls/error.h"
29 
30 #include <string.h>
31 
32 #if defined(MBEDTLS_RSA_C)
33 #include "mbedtls/rsa.h"
34 #endif
35 #if defined(MBEDTLS_ECP_C)
36 #include "mbedtls/bignum.h"
37 #include "mbedtls/ecp.h"
38 #include "mbedtls/platform_util.h"
39 #endif
40 #if defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECP_C)
41 #include "pkwrite.h"
42 #endif
43 #if defined(MBEDTLS_ECDSA_C)
44 #include "mbedtls/ecdsa.h"
45 #endif
46 #if defined(MBEDTLS_PEM_WRITE_C)
47 #include "mbedtls/pem.h"
48 #endif
49 
50 #if defined(MBEDTLS_USE_PSA_CRYPTO)
51 #include "psa/crypto.h"
52 #include "mbedtls/psa_util.h"
53 #endif
54 #include "mbedtls/platform.h"
55 
56 #if defined(MBEDTLS_RSA_C)
57 /*
58  *  RSAPublicKey ::= SEQUENCE {
59  *      modulus           INTEGER,  -- n
60  *      publicExponent    INTEGER   -- e
61  *  }
62  */
pk_write_rsa_pubkey(unsigned char ** p,unsigned char * start,mbedtls_rsa_context * rsa)63 static int pk_write_rsa_pubkey(unsigned char **p, unsigned char *start,
64                                mbedtls_rsa_context *rsa)
65 {
66     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
67     size_t len = 0;
68     mbedtls_mpi T;
69 
70     mbedtls_mpi_init(&T);
71 
72     /* Export E */
73     if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &T)) != 0 ||
74         (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
75         goto end_of_export;
76     }
77     len += ret;
78 
79     /* Export N */
80     if ((ret = mbedtls_rsa_export(rsa, &T, NULL, NULL, NULL, NULL)) != 0 ||
81         (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) {
82         goto end_of_export;
83     }
84     len += ret;
85 
86 end_of_export:
87 
88     mbedtls_mpi_free(&T);
89     if (ret < 0) {
90         return ret;
91     }
92 
93     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
94     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED |
95                                                      MBEDTLS_ASN1_SEQUENCE));
96 
97     return (int) len;
98 }
99 #endif /* MBEDTLS_RSA_C */
100 
101 #if defined(MBEDTLS_ECP_C)
102 /*
103  * EC public key is an EC point
104  */
pk_write_ec_pubkey(unsigned char ** p,unsigned char * start,mbedtls_ecp_keypair * ec)105 static int pk_write_ec_pubkey(unsigned char **p, unsigned char *start,
106                               mbedtls_ecp_keypair *ec)
107 {
108     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
109     size_t len = 0;
110     unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN];
111 
112     if ((ret = mbedtls_ecp_point_write_binary(&ec->grp, &ec->Q,
113                                               MBEDTLS_ECP_PF_UNCOMPRESSED,
114                                               &len, buf, sizeof(buf))) != 0) {
115         return ret;
116     }
117 
118     if (*p < start || (size_t) (*p - start) < len) {
119         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
120     }
121 
122     *p -= len;
123     memcpy(*p, buf, len);
124 
125     return (int) len;
126 }
127 
128 /*
129  * ECParameters ::= CHOICE {
130  *   namedCurve         OBJECT IDENTIFIER
131  * }
132  */
pk_write_ec_param(unsigned char ** p,unsigned char * start,mbedtls_ecp_keypair * ec)133 static int pk_write_ec_param(unsigned char **p, unsigned char *start,
134                              mbedtls_ecp_keypair *ec)
135 {
136     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
137     size_t len = 0;
138     const char *oid;
139     size_t oid_len;
140 
141     if ((ret = mbedtls_oid_get_oid_by_ec_grp(ec->grp.id, &oid, &oid_len)) != 0) {
142         return ret;
143     }
144 
145     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, oid_len));
146 
147     return (int) len;
148 }
149 
150 /*
151  * privateKey  OCTET STRING -- always of length ceil(log2(n)/8)
152  */
pk_write_ec_private(unsigned char ** p,unsigned char * start,mbedtls_ecp_keypair * ec)153 static int pk_write_ec_private(unsigned char **p, unsigned char *start,
154                                mbedtls_ecp_keypair *ec)
155 {
156     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
157     size_t byte_length = (ec->grp.pbits + 7) / 8;
158     unsigned char tmp[MBEDTLS_ECP_MAX_BYTES];
159 
160     ret = mbedtls_ecp_write_key(ec, tmp, byte_length);
161     if (ret != 0) {
162         goto exit;
163     }
164     ret = mbedtls_asn1_write_octet_string(p, start, tmp, byte_length);
165 
166 exit:
167     mbedtls_platform_zeroize(tmp, byte_length);
168     return ret;
169 }
170 #endif /* MBEDTLS_ECP_C */
171 
mbedtls_pk_write_pubkey(unsigned char ** p,unsigned char * start,const mbedtls_pk_context * key)172 int mbedtls_pk_write_pubkey(unsigned char **p, unsigned char *start,
173                             const mbedtls_pk_context *key)
174 {
175     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
176     size_t len = 0;
177 
178 #if defined(MBEDTLS_RSA_C)
179     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) {
180         MBEDTLS_ASN1_CHK_ADD(len, pk_write_rsa_pubkey(p, start, mbedtls_pk_rsa(*key)));
181     } else
182 #endif
183 #if defined(MBEDTLS_ECP_C)
184     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) {
185         MBEDTLS_ASN1_CHK_ADD(len, pk_write_ec_pubkey(p, start, mbedtls_pk_ec(*key)));
186     } else
187 #endif
188 #if defined(MBEDTLS_USE_PSA_CRYPTO)
189     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_OPAQUE) {
190         size_t buffer_size;
191         mbedtls_svc_key_id_t *key_id = (mbedtls_svc_key_id_t *) key->pk_ctx;
192 
193         if (*p < start) {
194             return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
195         }
196 
197         buffer_size = (size_t) (*p - start);
198         if (psa_export_public_key(*key_id, start, buffer_size, &len)
199             != PSA_SUCCESS) {
200             return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
201         } else {
202             *p -= len;
203             memmove(*p, start, len);
204         }
205     } else
206 #endif /* MBEDTLS_USE_PSA_CRYPTO */
207     return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
208 
209     return (int) len;
210 }
211 
mbedtls_pk_write_pubkey_der(const mbedtls_pk_context * key,unsigned char * buf,size_t size)212 int mbedtls_pk_write_pubkey_der(const mbedtls_pk_context *key, unsigned char *buf, size_t size)
213 {
214     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
215     unsigned char *c;
216     size_t len = 0, par_len = 0, oid_len;
217     mbedtls_pk_type_t pk_type;
218     const char *oid;
219 
220     if (size == 0) {
221         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
222     }
223 
224     c = buf + size;
225 
226     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_pk_write_pubkey(&c, buf, key));
227 
228     if (c - buf < 1) {
229         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
230     }
231 
232     /*
233      *  SubjectPublicKeyInfo  ::=  SEQUENCE  {
234      *       algorithm            AlgorithmIdentifier,
235      *       subjectPublicKey     BIT STRING }
236      */
237     *--c = 0;
238     len += 1;
239 
240     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len));
241     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_BIT_STRING));
242 
243     pk_type = mbedtls_pk_get_type(key);
244 #if defined(MBEDTLS_ECP_C)
245     if (pk_type == MBEDTLS_PK_ECKEY) {
246         MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, mbedtls_pk_ec(*key)));
247     }
248 #endif
249 #if defined(MBEDTLS_USE_PSA_CRYPTO)
250     if (pk_type == MBEDTLS_PK_OPAQUE) {
251         psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
252         psa_key_type_t key_type;
253         mbedtls_svc_key_id_t key_id;
254         psa_ecc_family_t curve;
255         size_t bits;
256 
257         key_id = *((mbedtls_svc_key_id_t *) key->pk_ctx);
258         if (PSA_SUCCESS != psa_get_key_attributes(key_id, &attributes)) {
259             return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
260         }
261         key_type = psa_get_key_type(&attributes);
262         bits = psa_get_key_bits(&attributes);
263         psa_reset_key_attributes(&attributes);
264 
265         if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type)) {
266             curve = PSA_KEY_TYPE_ECC_GET_FAMILY(key_type);
267             if (curve == 0) {
268                 return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
269             }
270 
271             ret = mbedtls_psa_get_ecc_oid_from_id(curve, bits,
272                                                   &oid, &oid_len);
273             if (ret != 0) {
274                 return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
275             }
276 
277             /* Write EC algorithm parameters; that's akin
278              * to pk_write_ec_param() above. */
279             MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_oid(&c, buf,
280                                                                  oid,
281                                                                  oid_len));
282 
283             /* The rest of the function works as for legacy EC contexts. */
284             pk_type = MBEDTLS_PK_ECKEY;
285         } else if (PSA_KEY_TYPE_IS_RSA(key_type)) {
286             /* The rest of the function works as for legacy RSA contexts. */
287             pk_type = MBEDTLS_PK_RSA;
288         } else {
289             return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
290         }
291     }
292 #endif /* MBEDTLS_USE_PSA_CRYPTO */
293 
294     if ((ret = mbedtls_oid_get_oid_by_pk_alg(pk_type, &oid,
295                                              &oid_len)) != 0) {
296         return ret;
297     }
298 
299     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier(&c, buf, oid, oid_len,
300                                                                       par_len));
301 
302     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len));
303     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED |
304                                                      MBEDTLS_ASN1_SEQUENCE));
305 
306     return (int) len;
307 }
308 
mbedtls_pk_write_key_der(const mbedtls_pk_context * key,unsigned char * buf,size_t size)309 int mbedtls_pk_write_key_der(const mbedtls_pk_context *key, unsigned char *buf, size_t size)
310 {
311     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
312     unsigned char *c;
313     size_t len = 0;
314 
315     if (size == 0) {
316         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
317     }
318 
319     c = buf + size;
320 
321 #if defined(MBEDTLS_RSA_C)
322     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) {
323         mbedtls_mpi T; /* Temporary holding the exported parameters */
324         mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*key);
325 
326         /*
327          * Export the parameters one after another to avoid simultaneous copies.
328          */
329 
330         mbedtls_mpi_init(&T);
331 
332         /* Export QP */
333         if ((ret = mbedtls_rsa_export_crt(rsa, NULL, NULL, &T)) != 0 ||
334             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
335             goto end_of_export;
336         }
337         len += ret;
338 
339         /* Export DQ */
340         if ((ret = mbedtls_rsa_export_crt(rsa, NULL, &T, NULL)) != 0 ||
341             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
342             goto end_of_export;
343         }
344         len += ret;
345 
346         /* Export DP */
347         if ((ret = mbedtls_rsa_export_crt(rsa, &T, NULL, NULL)) != 0 ||
348             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
349             goto end_of_export;
350         }
351         len += ret;
352 
353         /* Export Q */
354         if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
355                                       &T, NULL, NULL)) != 0 ||
356             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
357             goto end_of_export;
358         }
359         len += ret;
360 
361         /* Export P */
362         if ((ret = mbedtls_rsa_export(rsa, NULL, &T,
363                                       NULL, NULL, NULL)) != 0 ||
364             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
365             goto end_of_export;
366         }
367         len += ret;
368 
369         /* Export D */
370         if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
371                                       NULL, &T, NULL)) != 0 ||
372             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
373             goto end_of_export;
374         }
375         len += ret;
376 
377         /* Export E */
378         if ((ret = mbedtls_rsa_export(rsa, NULL, NULL,
379                                       NULL, NULL, &T)) != 0 ||
380             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
381             goto end_of_export;
382         }
383         len += ret;
384 
385         /* Export N */
386         if ((ret = mbedtls_rsa_export(rsa, &T, NULL,
387                                       NULL, NULL, NULL)) != 0 ||
388             (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) {
389             goto end_of_export;
390         }
391         len += ret;
392 
393 end_of_export:
394 
395         mbedtls_mpi_free(&T);
396         if (ret < 0) {
397             return ret;
398         }
399 
400         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, 0));
401         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len));
402         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c,
403                                                          buf, MBEDTLS_ASN1_CONSTRUCTED |
404                                                          MBEDTLS_ASN1_SEQUENCE));
405     } else
406 #endif /* MBEDTLS_RSA_C */
407 #if defined(MBEDTLS_ECP_C)
408     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) {
409         mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*key);
410         size_t pub_len = 0, par_len = 0;
411 
412         /*
413          * RFC 5915, or SEC1 Appendix C.4
414          *
415          * ECPrivateKey ::= SEQUENCE {
416          *      version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
417          *      privateKey     OCTET STRING,
418          *      parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
419          *      publicKey  [1] BIT STRING OPTIONAL
420          *    }
421          */
422 
423         /* publicKey */
424         MBEDTLS_ASN1_CHK_ADD(pub_len, pk_write_ec_pubkey(&c, buf, ec));
425 
426         if (c - buf < 1) {
427             return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
428         }
429         *--c = 0;
430         pub_len += 1;
431 
432         MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_len(&c, buf, pub_len));
433         MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_BIT_STRING));
434 
435         MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_len(&c, buf, pub_len));
436         MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_tag(&c, buf,
437                                                              MBEDTLS_ASN1_CONTEXT_SPECIFIC |
438                                                              MBEDTLS_ASN1_CONSTRUCTED | 1));
439         len += pub_len;
440 
441         /* parameters */
442         MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, ec));
443 
444         MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_len(&c, buf, par_len));
445         MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_tag(&c, buf,
446                                                              MBEDTLS_ASN1_CONTEXT_SPECIFIC |
447                                                              MBEDTLS_ASN1_CONSTRUCTED | 0));
448         len += par_len;
449 
450         /* privateKey */
451         MBEDTLS_ASN1_CHK_ADD(len, pk_write_ec_private(&c, buf, ec));
452 
453         /* version */
454         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, 1));
455 
456         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len));
457         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED |
458                                                          MBEDTLS_ASN1_SEQUENCE));
459     } else
460 #endif /* MBEDTLS_ECP_C */
461     return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
462 
463     return (int) len;
464 }
465 
466 #if defined(MBEDTLS_PEM_WRITE_C)
467 
468 #define PEM_BEGIN_PUBLIC_KEY    "-----BEGIN PUBLIC KEY-----\n"
469 #define PEM_END_PUBLIC_KEY      "-----END PUBLIC KEY-----\n"
470 
471 #define PEM_BEGIN_PRIVATE_KEY_RSA   "-----BEGIN RSA PRIVATE KEY-----\n"
472 #define PEM_END_PRIVATE_KEY_RSA     "-----END RSA PRIVATE KEY-----\n"
473 #define PEM_BEGIN_PRIVATE_KEY_EC    "-----BEGIN EC PRIVATE KEY-----\n"
474 #define PEM_END_PRIVATE_KEY_EC      "-----END EC PRIVATE KEY-----\n"
475 
476 #define PUB_DER_MAX_BYTES                                                   \
477     (MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES > MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ? \
478      MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES : MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES)
479 #define PRV_DER_MAX_BYTES                                                   \
480     (MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES > MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ? \
481      MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES : MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES)
482 
mbedtls_pk_write_pubkey_pem(const mbedtls_pk_context * key,unsigned char * buf,size_t size)483 int mbedtls_pk_write_pubkey_pem(const mbedtls_pk_context *key, unsigned char *buf, size_t size)
484 {
485     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
486     unsigned char output_buf[PUB_DER_MAX_BYTES];
487     size_t olen = 0;
488 
489     if ((ret = mbedtls_pk_write_pubkey_der(key, output_buf,
490                                            sizeof(output_buf))) < 0) {
491         return ret;
492     }
493 
494     if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY,
495                                         output_buf + sizeof(output_buf) - ret,
496                                         ret, buf, size, &olen)) != 0) {
497         return ret;
498     }
499 
500     return 0;
501 }
502 
mbedtls_pk_write_key_pem(const mbedtls_pk_context * key,unsigned char * buf,size_t size)503 int mbedtls_pk_write_key_pem(const mbedtls_pk_context *key, unsigned char *buf, size_t size)
504 {
505     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
506     unsigned char output_buf[PRV_DER_MAX_BYTES];
507     const char *begin, *end;
508     size_t olen = 0;
509 
510     if ((ret = mbedtls_pk_write_key_der(key, output_buf, sizeof(output_buf))) < 0) {
511         return ret;
512     }
513 
514 #if defined(MBEDTLS_RSA_C)
515     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) {
516         begin = PEM_BEGIN_PRIVATE_KEY_RSA;
517         end = PEM_END_PRIVATE_KEY_RSA;
518     } else
519 #endif
520 #if defined(MBEDTLS_ECP_C)
521     if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) {
522         begin = PEM_BEGIN_PRIVATE_KEY_EC;
523         end = PEM_END_PRIVATE_KEY_EC;
524     } else
525 #endif
526     return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
527 
528     if ((ret = mbedtls_pem_write_buffer(begin, end,
529                                         output_buf + sizeof(output_buf) - ret,
530                                         ret, buf, size, &olen)) != 0) {
531         return ret;
532     }
533 
534     return 0;
535 }
536 #endif /* MBEDTLS_PEM_WRITE_C */
537 
538 #endif /* MBEDTLS_PK_WRITE_C */
539