pembase64.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * Copyright (c) 2016 Christian Huitema <huitema@huitema.net>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. /*
  17. * Manage Base64 encoding.
  18. */
  19. #ifdef _WINDOWS
  20. #include "wincompat.h"
  21. #else
  22. #include <sys/time.h>
  23. #endif
  24. #include <errno.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <stdio.h>
  28. #include "picotls.h"
  29. #include "picotls/pembase64.h"
  30. static char ptls_base64_alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  31. 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  32. 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  33. 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
  34. static signed char ptls_base64_values[] = {
  35. /* 0x00 to 0x0F */
  36. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  37. /* 0x10 to 0x1F */
  38. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  39. /* 0x20 to 0x2F. '+' at 2B, '/' at 2F */
  40. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
  41. /* 0x30 to 0x3F -- digits 0 to 9 at 0x30 to 0x39*/
  42. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
  43. /* 0x40 to 0x4F -- chars 'A' to 'O' at 0x41 to 0x4F */
  44. -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  45. /* 0x50 to 0x5F -- chars 'P' to 'Z' at 0x50 to 0x5A */
  46. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
  47. /* 0x60 to 0x6F -- chars 'a' to 'o' at 0x61 to 0x6F */
  48. -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  49. /* 0x70 to 0x7F -- chars 'p' to 'z' at 0x70 to 0x7A */
  50. 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1};
  51. static void ptls_base64_cell(const uint8_t *data, char *text)
  52. {
  53. int n[4];
  54. n[0] = data[0] >> 2;
  55. n[1] = ((data[0] & 3) << 4) | (data[1] >> 4);
  56. n[2] = ((data[1] & 15) << 2) | (data[2] >> 6);
  57. n[3] = data[2] & 63;
  58. for (int i = 0; i < 4; i++) {
  59. text[i] = ptls_base64_alphabet[n[i]];
  60. }
  61. }
  62. size_t ptls_base64_howlong(size_t data_length)
  63. {
  64. return (((data_length + 2) / 3) * 4);
  65. }
  66. int ptls_base64_encode(const uint8_t *data, size_t data_len, char *ptls_base64_text)
  67. {
  68. int l = 0;
  69. int lt = 0;
  70. while ((data_len - l) >= 3) {
  71. ptls_base64_cell(data + l, ptls_base64_text + lt);
  72. l += 3;
  73. lt += 4;
  74. }
  75. switch (data_len - l) {
  76. case 0:
  77. break;
  78. case 1:
  79. ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2];
  80. ptls_base64_text[lt++] = ptls_base64_alphabet[(data[l] & 3) << 4];
  81. ptls_base64_text[lt++] = '=';
  82. ptls_base64_text[lt++] = '=';
  83. break;
  84. case 2:
  85. ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2];
  86. ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l] & 3) << 4) | (data[l + 1] >> 4)];
  87. ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l + 1] & 15) << 2)];
  88. ptls_base64_text[lt++] = '=';
  89. break;
  90. default:
  91. break;
  92. }
  93. ptls_base64_text[lt++] = 0;
  94. return lt;
  95. }
  96. /*
  97. * Take into input a line of text, so as to work by increments.
  98. * The intermediate text of the decoding is kept in a state variable.
  99. * The decoded data is accumulated in a PTLS buffer.
  100. * The parsing is consistent with the lax definition in RFC 7468
  101. */
  102. void ptls_base64_decode_init(ptls_base64_decode_state_t *state)
  103. {
  104. state->nbc = 0;
  105. state->nbo = 3;
  106. state->v = 0;
  107. state->status = PTLS_BASE64_DECODE_IN_PROGRESS;
  108. }
  109. int ptls_base64_decode(const char *text, ptls_base64_decode_state_t *state, ptls_buffer_t *buf)
  110. {
  111. int ret = 0;
  112. uint8_t decoded[3];
  113. size_t text_index = 0;
  114. int c;
  115. signed char vc;
  116. /* skip initial blanks */
  117. while (text[text_index] != 0) {
  118. c = text[text_index];
  119. if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
  120. text_index++;
  121. } else {
  122. break;
  123. }
  124. }
  125. while (text[text_index] != 0 && ret == 0 && state->status == PTLS_BASE64_DECODE_IN_PROGRESS) {
  126. c = text[text_index++];
  127. vc = 0 < c && c < 0x7f ? ptls_base64_values[c] : -1;
  128. if (vc == -1) {
  129. if (state->nbc == 2 && c == '=' && text[text_index] == '=') {
  130. state->nbc = 4;
  131. text_index++;
  132. state->nbo = 1;
  133. state->v <<= 12;
  134. } else if (state->nbc == 3 && c == '=') {
  135. state->nbc = 4;
  136. state->nbo = 2;
  137. state->v <<= 6;
  138. } else {
  139. /* Skip final blanks */
  140. for (--text_index; text[text_index] != 0; ++text_index) {
  141. c = text[text_index];
  142. if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C))
  143. break;
  144. }
  145. /* Should now be at end of buffer */
  146. if (text[text_index] == 0) {
  147. break;
  148. } else {
  149. /* Not at end of buffer, signal a decoding error */
  150. state->nbo = 0;
  151. state->status = PTLS_BASE64_DECODE_FAILED;
  152. ret = PTLS_ERROR_INCORRECT_BASE64;
  153. }
  154. }
  155. } else {
  156. state->nbc++;
  157. state->v <<= 6;
  158. state->v |= vc;
  159. }
  160. if (ret == 0 && state->nbc == 4) {
  161. /* Convert to up to 3 octets */
  162. for (int j = 0; j < state->nbo; j++) {
  163. decoded[j] = (uint8_t)(state->v >> (8 * (2 - j)));
  164. }
  165. ret = ptls_buffer__do_pushv(buf, decoded, state->nbo);
  166. if (ret == 0) {
  167. /* test for fin or continuation */
  168. if (state->nbo < 3) {
  169. /* Check that there are only trainling blanks on this line */
  170. while (text[text_index] != 0) {
  171. c = text[text_index++];
  172. if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C) {
  173. continue;
  174. }
  175. }
  176. if (text[text_index] == 0) {
  177. state->status = PTLS_BASE64_DECODE_DONE;
  178. } else {
  179. state->status = PTLS_BASE64_DECODE_FAILED;
  180. ret = PTLS_ERROR_INCORRECT_BASE64;
  181. }
  182. break;
  183. } else {
  184. state->v = 0;
  185. state->nbo = 3;
  186. state->nbc = 0;
  187. }
  188. }
  189. }
  190. }
  191. return ret;
  192. }
  193. /*
  194. * Reading a PEM file, to get an object:
  195. *
  196. * - Find first object, get the object name.
  197. * - If object label is what the application expects, parse, else skip to end.
  198. *
  199. * The following labels are defined in RFC 7468:
  200. *
  201. * Sec. Label ASN.1 Type Reference Module
  202. * ----+----------------------+-----------------------+---------+----------
  203. * 5 CERTIFICATE Certificate [RFC5280] id-pkix1-e
  204. * 6 X509 CRL CertificateList [RFC5280] id-pkix1-e
  205. * 7 CERTIFICATE REQUEST CertificationRequest [RFC2986] id-pkcs10
  206. * 8 PKCS7 ContentInfo [RFC2315] id-pkcs7*
  207. * 9 CMS ContentInfo [RFC5652] id-cms2004
  208. * 10 PRIVATE KEY PrivateKeyInfo ::= [RFC5208] id-pkcs8
  209. * OneAsymmetricKey [RFC5958] id-aKPV1
  210. * 11 ENCRYPTED PRIVATE KEY EncryptedPrivateKeyInfo [RFC5958] id-aKPV1
  211. * 12 ATTRIBUTE CERTIFICATE AttributeCertificate [RFC5755] id-acv2
  212. * 13 PUBLIC KEY SubjectPublicKeyInfo [RFC5280] id-pkix1-e
  213. */
  214. static int ptls_compare_separator_line(const char *line, const char *begin_or_end, const char *label)
  215. {
  216. int ret = strncmp(line, "-----", 5);
  217. size_t text_index = 5;
  218. if (ret == 0) {
  219. size_t begin_or_end_length = strlen(begin_or_end);
  220. ret = strncmp(line + text_index, begin_or_end, begin_or_end_length);
  221. text_index += begin_or_end_length;
  222. }
  223. if (ret == 0) {
  224. ret = line[text_index] - ' ';
  225. text_index++;
  226. }
  227. if (ret == 0) {
  228. size_t label_length = strlen(label);
  229. ret = strncmp(line + text_index, label, label_length);
  230. text_index += label_length;
  231. }
  232. if (ret == 0) {
  233. ret = strncmp(line + text_index, "-----", 5);
  234. }
  235. return ret;
  236. }
  237. static int ptls_get_pem_object(FILE *F, const char *label, ptls_buffer_t *buf)
  238. {
  239. int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND;
  240. char line[256];
  241. ptls_base64_decode_state_t state;
  242. /* Get the label on a line by itself */
  243. while (fgets(line, 256, F)) {
  244. if (ptls_compare_separator_line(line, "BEGIN", label) == 0) {
  245. ret = 0;
  246. ptls_base64_decode_init(&state);
  247. break;
  248. }
  249. }
  250. /* Get the data in the buffer */
  251. while (ret == 0 && fgets(line, 256, F)) {
  252. if (ptls_compare_separator_line(line, "END", label) == 0) {
  253. if (state.status == PTLS_BASE64_DECODE_DONE || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS && state.nbc == 0)) {
  254. ret = 0;
  255. } else {
  256. ret = PTLS_ERROR_INCORRECT_BASE64;
  257. }
  258. break;
  259. } else {
  260. ret = ptls_base64_decode(line, &state, buf);
  261. }
  262. }
  263. return ret;
  264. }
  265. int ptls_load_pem_objects(char const *pem_fname, const char *label, ptls_iovec_t *list, size_t list_max, size_t *nb_objects)
  266. {
  267. FILE *F;
  268. int ret = 0;
  269. size_t count = 0;
  270. #ifdef _WINDOWS
  271. errno_t err = fopen_s(&F, pem_fname, "r");
  272. if (err != 0) {
  273. ret = -1;
  274. }
  275. #else
  276. F = fopen(pem_fname, "r");
  277. if (F == NULL) {
  278. ret = -1;
  279. }
  280. #endif
  281. *nb_objects = 0;
  282. if (ret == 0) {
  283. while (count < list_max) {
  284. ptls_buffer_t buf;
  285. ptls_buffer_init(&buf, "", 0);
  286. ret = ptls_get_pem_object(F, label, &buf);
  287. if (ret == 0) {
  288. if (buf.off > 0 && buf.is_allocated) {
  289. list[count].base = buf.base;
  290. list[count].len = buf.off;
  291. count++;
  292. } else {
  293. ptls_buffer_dispose(&buf);
  294. }
  295. } else {
  296. ptls_buffer_dispose(&buf);
  297. break;
  298. }
  299. }
  300. }
  301. if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0) {
  302. ret = 0;
  303. }
  304. *nb_objects = count;
  305. if (F != NULL) {
  306. fclose(F);
  307. }
  308. return ret;
  309. }
  310. #define PTLS_MAX_CERTS_IN_CONTEXT 16
  311. int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file)
  312. {
  313. int ret = 0;
  314. ctx->certificates.list = (ptls_iovec_t *)malloc(PTLS_MAX_CERTS_IN_CONTEXT * sizeof(ptls_iovec_t));
  315. if (ctx->certificates.list == NULL) {
  316. ret = PTLS_ERROR_NO_MEMORY;
  317. } else {
  318. ret = ptls_load_pem_objects(cert_pem_file, "CERTIFICATE", ctx->certificates.list, PTLS_MAX_CERTS_IN_CONTEXT,
  319. &ctx->certificates.count);
  320. }
  321. return ret;
  322. }