pkwrite.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * Public Key layer for writing key files and structures
  3. *
  4. * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
  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. * This file is part of mbed TLS (https://tls.mbed.org)
  20. */
  21. #if !defined(MBEDTLS_CONFIG_FILE)
  22. #include "mbedtls/config.h"
  23. #else
  24. #include MBEDTLS_CONFIG_FILE
  25. #endif
  26. #if defined(MBEDTLS_PK_WRITE_C)
  27. #include "mbedtls/pk.h"
  28. #include "mbedtls/asn1write.h"
  29. #include "mbedtls/oid.h"
  30. #include <string.h>
  31. #if defined(MBEDTLS_RSA_C)
  32. #include "mbedtls/rsa.h"
  33. #endif
  34. #if defined(MBEDTLS_ECP_C)
  35. #include "mbedtls/ecp.h"
  36. #endif
  37. #if defined(MBEDTLS_ECDSA_C)
  38. #include "mbedtls/ecdsa.h"
  39. #endif
  40. #if defined(MBEDTLS_PEM_WRITE_C)
  41. #include "mbedtls/pem.h"
  42. #endif
  43. #if defined(MBEDTLS_PLATFORM_C)
  44. #include "mbedtls/platform.h"
  45. #else
  46. #include <stdlib.h>
  47. #define mbedtls_calloc calloc
  48. #define mbedtls_free free
  49. #endif
  50. #if defined(MBEDTLS_RSA_C)
  51. /*
  52. * RSAPublicKey ::= SEQUENCE {
  53. * modulus INTEGER, -- n
  54. * publicExponent INTEGER -- e
  55. * }
  56. */
  57. static int pk_write_rsa_pubkey( unsigned char **p, unsigned char *start,
  58. mbedtls_rsa_context *rsa )
  59. {
  60. int ret;
  61. size_t len = 0;
  62. mbedtls_mpi T;
  63. mbedtls_mpi_init( &T );
  64. /* Export E */
  65. if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL, NULL, NULL, &T ) ) != 0 ||
  66. ( ret = mbedtls_asn1_write_mpi( p, start, &T ) ) < 0 )
  67. goto end_of_export;
  68. len += ret;
  69. /* Export N */
  70. if ( ( ret = mbedtls_rsa_export( rsa, &T, NULL, NULL, NULL, NULL ) ) != 0 ||
  71. ( ret = mbedtls_asn1_write_mpi( p, start, &T ) ) < 0 )
  72. goto end_of_export;
  73. len += ret;
  74. end_of_export:
  75. mbedtls_mpi_free( &T );
  76. if( ret < 0 )
  77. return( ret );
  78. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
  79. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_CONSTRUCTED |
  80. MBEDTLS_ASN1_SEQUENCE ) );
  81. return( (int) len );
  82. }
  83. #endif /* MBEDTLS_RSA_C */
  84. #if defined(MBEDTLS_ECP_C)
  85. /*
  86. * EC public key is an EC point
  87. */
  88. static int pk_write_ec_pubkey( unsigned char **p, unsigned char *start,
  89. mbedtls_ecp_keypair *ec )
  90. {
  91. int ret;
  92. size_t len = 0;
  93. unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN];
  94. if( ( ret = mbedtls_ecp_point_write_binary( &ec->grp, &ec->Q,
  95. MBEDTLS_ECP_PF_UNCOMPRESSED,
  96. &len, buf, sizeof( buf ) ) ) != 0 )
  97. {
  98. return( ret );
  99. }
  100. if( *p < start || (size_t)( *p - start ) < len )
  101. return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
  102. *p -= len;
  103. memcpy( *p, buf, len );
  104. return( (int) len );
  105. }
  106. /*
  107. * ECParameters ::= CHOICE {
  108. * namedCurve OBJECT IDENTIFIER
  109. * }
  110. */
  111. static int pk_write_ec_param( unsigned char **p, unsigned char *start,
  112. mbedtls_ecp_keypair *ec )
  113. {
  114. int ret;
  115. size_t len = 0;
  116. const char *oid;
  117. size_t oid_len;
  118. if( ( ret = mbedtls_oid_get_oid_by_ec_grp( ec->grp.id, &oid, &oid_len ) ) != 0 )
  119. return( ret );
  120. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_oid( p, start, oid, oid_len ) );
  121. return( (int) len );
  122. }
  123. #endif /* MBEDTLS_ECP_C */
  124. int mbedtls_pk_write_pubkey( unsigned char **p, unsigned char *start,
  125. const mbedtls_pk_context *key )
  126. {
  127. int ret;
  128. size_t len = 0;
  129. #if defined(MBEDTLS_RSA_C)
  130. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
  131. MBEDTLS_ASN1_CHK_ADD( len, pk_write_rsa_pubkey( p, start, mbedtls_pk_rsa( *key ) ) );
  132. else
  133. #endif
  134. #if defined(MBEDTLS_ECP_C)
  135. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
  136. MBEDTLS_ASN1_CHK_ADD( len, pk_write_ec_pubkey( p, start, mbedtls_pk_ec( *key ) ) );
  137. else
  138. #endif
  139. return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
  140. return( (int) len );
  141. }
  142. int mbedtls_pk_write_pubkey_der( mbedtls_pk_context *key, unsigned char *buf, size_t size )
  143. {
  144. int ret;
  145. unsigned char *c;
  146. size_t len = 0, par_len = 0, oid_len;
  147. const char *oid;
  148. c = buf + size;
  149. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, key ) );
  150. if( c - buf < 1 )
  151. return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
  152. /*
  153. * SubjectPublicKeyInfo ::= SEQUENCE {
  154. * algorithm AlgorithmIdentifier,
  155. * subjectPublicKey BIT STRING }
  156. */
  157. *--c = 0;
  158. len += 1;
  159. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
  160. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_BIT_STRING ) );
  161. if( ( ret = mbedtls_oid_get_oid_by_pk_alg( mbedtls_pk_get_type( key ),
  162. &oid, &oid_len ) ) != 0 )
  163. {
  164. return( ret );
  165. }
  166. #if defined(MBEDTLS_ECP_C)
  167. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
  168. {
  169. MBEDTLS_ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, mbedtls_pk_ec( *key ) ) );
  170. }
  171. #endif
  172. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_algorithm_identifier( &c, buf, oid, oid_len,
  173. par_len ) );
  174. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
  175. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
  176. MBEDTLS_ASN1_SEQUENCE ) );
  177. return( (int) len );
  178. }
  179. int mbedtls_pk_write_key_der( mbedtls_pk_context *key, unsigned char *buf, size_t size )
  180. {
  181. int ret;
  182. unsigned char *c = buf + size;
  183. size_t len = 0;
  184. #if defined(MBEDTLS_RSA_C)
  185. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
  186. {
  187. mbedtls_mpi T; /* Temporary holding the exported parameters */
  188. mbedtls_rsa_context *rsa = mbedtls_pk_rsa( *key );
  189. /*
  190. * Export the parameters one after another to avoid simultaneous copies.
  191. */
  192. mbedtls_mpi_init( &T );
  193. /* Export QP */
  194. if( ( ret = mbedtls_rsa_export_crt( rsa, NULL, NULL, &T ) ) != 0 ||
  195. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  196. goto end_of_export;
  197. len += ret;
  198. /* Export DQ */
  199. if( ( ret = mbedtls_rsa_export_crt( rsa, NULL, &T, NULL ) ) != 0 ||
  200. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  201. goto end_of_export;
  202. len += ret;
  203. /* Export DP */
  204. if( ( ret = mbedtls_rsa_export_crt( rsa, &T, NULL, NULL ) ) != 0 ||
  205. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  206. goto end_of_export;
  207. len += ret;
  208. /* Export Q */
  209. if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
  210. &T, NULL, NULL ) ) != 0 ||
  211. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  212. goto end_of_export;
  213. len += ret;
  214. /* Export P */
  215. if ( ( ret = mbedtls_rsa_export( rsa, NULL, &T,
  216. NULL, NULL, NULL ) ) != 0 ||
  217. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  218. goto end_of_export;
  219. len += ret;
  220. /* Export D */
  221. if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
  222. NULL, &T, NULL ) ) != 0 ||
  223. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  224. goto end_of_export;
  225. len += ret;
  226. /* Export E */
  227. if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
  228. NULL, NULL, &T ) ) != 0 ||
  229. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  230. goto end_of_export;
  231. len += ret;
  232. /* Export N */
  233. if ( ( ret = mbedtls_rsa_export( rsa, &T, NULL,
  234. NULL, NULL, NULL ) ) != 0 ||
  235. ( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
  236. goto end_of_export;
  237. len += ret;
  238. end_of_export:
  239. mbedtls_mpi_free( &T );
  240. if( ret < 0 )
  241. return( ret );
  242. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, 0 ) );
  243. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
  244. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c,
  245. buf, MBEDTLS_ASN1_CONSTRUCTED |
  246. MBEDTLS_ASN1_SEQUENCE ) );
  247. }
  248. else
  249. #endif /* MBEDTLS_RSA_C */
  250. #if defined(MBEDTLS_ECP_C)
  251. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
  252. {
  253. mbedtls_ecp_keypair *ec = mbedtls_pk_ec( *key );
  254. size_t pub_len = 0, par_len = 0;
  255. /*
  256. * RFC 5915, or SEC1 Appendix C.4
  257. *
  258. * ECPrivateKey ::= SEQUENCE {
  259. * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
  260. * privateKey OCTET STRING,
  261. * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
  262. * publicKey [1] BIT STRING OPTIONAL
  263. * }
  264. */
  265. /* publicKey */
  266. MBEDTLS_ASN1_CHK_ADD( pub_len, pk_write_ec_pubkey( &c, buf, ec ) );
  267. if( c - buf < 1 )
  268. return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
  269. *--c = 0;
  270. pub_len += 1;
  271. MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_len( &c, buf, pub_len ) );
  272. MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_BIT_STRING ) );
  273. MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_len( &c, buf, pub_len ) );
  274. MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_tag( &c, buf,
  275. MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1 ) );
  276. len += pub_len;
  277. /* parameters */
  278. MBEDTLS_ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, ec ) );
  279. MBEDTLS_ASN1_CHK_ADD( par_len, mbedtls_asn1_write_len( &c, buf, par_len ) );
  280. MBEDTLS_ASN1_CHK_ADD( par_len, mbedtls_asn1_write_tag( &c, buf,
  281. MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
  282. len += par_len;
  283. /* privateKey: write as MPI then fix tag */
  284. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, buf, &ec->d ) );
  285. *c = MBEDTLS_ASN1_OCTET_STRING;
  286. /* version */
  287. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, 1 ) );
  288. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
  289. MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
  290. MBEDTLS_ASN1_SEQUENCE ) );
  291. }
  292. else
  293. #endif /* MBEDTLS_ECP_C */
  294. return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
  295. return( (int) len );
  296. }
  297. #if defined(MBEDTLS_PEM_WRITE_C)
  298. #define PEM_BEGIN_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\n"
  299. #define PEM_END_PUBLIC_KEY "-----END PUBLIC KEY-----\n"
  300. #define PEM_BEGIN_PRIVATE_KEY_RSA "-----BEGIN RSA PRIVATE KEY-----\n"
  301. #define PEM_END_PRIVATE_KEY_RSA "-----END RSA PRIVATE KEY-----\n"
  302. #define PEM_BEGIN_PRIVATE_KEY_EC "-----BEGIN EC PRIVATE KEY-----\n"
  303. #define PEM_END_PRIVATE_KEY_EC "-----END EC PRIVATE KEY-----\n"
  304. /*
  305. * Max sizes of key per types. Shown as tag + len (+ content).
  306. */
  307. #if defined(MBEDTLS_RSA_C)
  308. /*
  309. * RSA public keys:
  310. * SubjectPublicKeyInfo ::= SEQUENCE { 1 + 3
  311. * algorithm AlgorithmIdentifier, 1 + 1 (sequence)
  312. * + 1 + 1 + 9 (rsa oid)
  313. * + 1 + 1 (params null)
  314. * subjectPublicKey BIT STRING } 1 + 3 + (1 + below)
  315. * RSAPublicKey ::= SEQUENCE { 1 + 3
  316. * modulus INTEGER, -- n 1 + 3 + MPI_MAX + 1
  317. * publicExponent INTEGER -- e 1 + 3 + MPI_MAX + 1
  318. * }
  319. */
  320. #define RSA_PUB_DER_MAX_BYTES 38 + 2 * MBEDTLS_MPI_MAX_SIZE
  321. /*
  322. * RSA private keys:
  323. * RSAPrivateKey ::= SEQUENCE { 1 + 3
  324. * version Version, 1 + 1 + 1
  325. * modulus INTEGER, 1 + 3 + MPI_MAX + 1
  326. * publicExponent INTEGER, 1 + 3 + MPI_MAX + 1
  327. * privateExponent INTEGER, 1 + 3 + MPI_MAX + 1
  328. * prime1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
  329. * prime2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
  330. * exponent1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
  331. * exponent2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
  332. * coefficient INTEGER, 1 + 3 + MPI_MAX / 2 + 1
  333. * otherPrimeInfos OtherPrimeInfos OPTIONAL 0 (not supported)
  334. * }
  335. */
  336. #define MPI_MAX_SIZE_2 MBEDTLS_MPI_MAX_SIZE / 2 + \
  337. MBEDTLS_MPI_MAX_SIZE % 2
  338. #define RSA_PRV_DER_MAX_BYTES 47 + 3 * MBEDTLS_MPI_MAX_SIZE \
  339. + 5 * MPI_MAX_SIZE_2
  340. #else /* MBEDTLS_RSA_C */
  341. #define RSA_PUB_DER_MAX_BYTES 0
  342. #define RSA_PRV_DER_MAX_BYTES 0
  343. #endif /* MBEDTLS_RSA_C */
  344. #if defined(MBEDTLS_ECP_C)
  345. /*
  346. * EC public keys:
  347. * SubjectPublicKeyInfo ::= SEQUENCE { 1 + 2
  348. * algorithm AlgorithmIdentifier, 1 + 1 (sequence)
  349. * + 1 + 1 + 7 (ec oid)
  350. * + 1 + 1 + 9 (namedCurve oid)
  351. * subjectPublicKey BIT STRING 1 + 2 + 1 [1]
  352. * + 1 (point format) [1]
  353. * + 2 * ECP_MAX (coords) [1]
  354. * }
  355. */
  356. #define ECP_PUB_DER_MAX_BYTES 30 + 2 * MBEDTLS_ECP_MAX_BYTES
  357. /*
  358. * EC private keys:
  359. * ECPrivateKey ::= SEQUENCE { 1 + 2
  360. * version INTEGER , 1 + 1 + 1
  361. * privateKey OCTET STRING, 1 + 1 + ECP_MAX
  362. * parameters [0] ECParameters OPTIONAL, 1 + 1 + (1 + 1 + 9)
  363. * publicKey [1] BIT STRING OPTIONAL 1 + 2 + [1] above
  364. * }
  365. */
  366. #define ECP_PRV_DER_MAX_BYTES 29 + 3 * MBEDTLS_ECP_MAX_BYTES
  367. #else /* MBEDTLS_ECP_C */
  368. #define ECP_PUB_DER_MAX_BYTES 0
  369. #define ECP_PRV_DER_MAX_BYTES 0
  370. #endif /* MBEDTLS_ECP_C */
  371. #define PUB_DER_MAX_BYTES RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \
  372. RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES
  373. #define PRV_DER_MAX_BYTES RSA_PRV_DER_MAX_BYTES > ECP_PRV_DER_MAX_BYTES ? \
  374. RSA_PRV_DER_MAX_BYTES : ECP_PRV_DER_MAX_BYTES
  375. int mbedtls_pk_write_pubkey_pem( mbedtls_pk_context *key, unsigned char *buf, size_t size )
  376. {
  377. int ret;
  378. unsigned char output_buf[PUB_DER_MAX_BYTES];
  379. size_t olen = 0;
  380. if( ( ret = mbedtls_pk_write_pubkey_der( key, output_buf,
  381. sizeof(output_buf) ) ) < 0 )
  382. {
  383. return( ret );
  384. }
  385. if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY,
  386. output_buf + sizeof(output_buf) - ret,
  387. ret, buf, size, &olen ) ) != 0 )
  388. {
  389. return( ret );
  390. }
  391. return( 0 );
  392. }
  393. int mbedtls_pk_write_key_pem( mbedtls_pk_context *key, unsigned char *buf, size_t size )
  394. {
  395. int ret;
  396. unsigned char output_buf[PRV_DER_MAX_BYTES];
  397. const char *begin, *end;
  398. size_t olen = 0;
  399. if( ( ret = mbedtls_pk_write_key_der( key, output_buf, sizeof(output_buf) ) ) < 0 )
  400. return( ret );
  401. #if defined(MBEDTLS_RSA_C)
  402. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
  403. {
  404. begin = PEM_BEGIN_PRIVATE_KEY_RSA;
  405. end = PEM_END_PRIVATE_KEY_RSA;
  406. }
  407. else
  408. #endif
  409. #if defined(MBEDTLS_ECP_C)
  410. if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
  411. {
  412. begin = PEM_BEGIN_PRIVATE_KEY_EC;
  413. end = PEM_END_PRIVATE_KEY_EC;
  414. }
  415. else
  416. #endif
  417. return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
  418. if( ( ret = mbedtls_pem_write_buffer( begin, end,
  419. output_buf + sizeof(output_buf) - ret,
  420. ret, buf, size, &olen ) ) != 0 )
  421. {
  422. return( ret );
  423. }
  424. return( 0 );
  425. }
  426. #endif /* MBEDTLS_PEM_WRITE_C */
  427. #endif /* MBEDTLS_PK_WRITE_C */