]> pd.if.org Git - zpackage/blobdiff - crypto/pem.c
commit files needed for zpm-fetchurl
[zpackage] / crypto / pem.c
diff --git a/crypto/pem.c b/crypto/pem.c
new file mode 100644 (file)
index 0000000..7d273f9
--- /dev/null
@@ -0,0 +1,368 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "tlse.h"
+
+struct pem_loc {
+       size_t start;
+       size_t len;
+       size_t end;
+};
+
+#if 0
+static const unsigned char *find_mem(const char *needle, size_t nlen,
+               const unsigned char *haystack, size_t hlen,
+               size_t *offset) {
+       size_t i;
+       const unsigned char *res = 0;
+
+       /* can't be found if it's longer */
+       if (nlen > hlen) {
+               return NULL;
+       }
+
+       /* empty string at beginning */
+       if (nlen == 0) {
+               *offset = 0;
+               return haystack;
+       }
+
+       for (i=0; i<hlen; i++) {
+               if (nlen + i > hlen) {
+                       /* can't match any more */
+                       break;
+               }
+               /* check for first byte */
+               if (needle[0] == haystack[i]) {
+                       if (memcmp(needle, haystack+i, nlen) == 0) {
+                               res = haystack+i;
+                               if (offset) {
+                                       *offset = i;
+                               }
+                               break;
+                       }
+               }
+       }
+       return res;
+}
+#endif
+
+#if 0
+/* fills in a struct pem_loc with offsets from in to the start
+ * of a pem, and one past the end, and the length of the pem data
+ */
+static const unsigned char *next_pem(const unsigned char *in, size_t len, struct pem_loc *pem) {
+       size_t i;
+       const unsigned char *next;
+
+       next = find_mem("BEGIN CERT", 10, in, len, &i);
+       if (!next) {
+               return NULL;
+       }
+
+       while (i<len && in[i] != '\n') {
+               i++;
+       }
+       if (i >= len) {
+               return NULL;
+       }
+       i++;
+       pem->start = i;
+
+       /* look for terminating line */
+       next = find_mem("END CERT", 8, in+pem->start, len, &i);
+       if (!next) {
+               return NULL;
+       }
+       i += pem->start;
+       pem->end = i;
+
+       /* find prev newline */
+       while (in[i] != '\n') {
+               i--;
+       }
+       pem->len = i - pem->start;
+       i += pem->start;
+
+       /* skip over the terminating line */
+       while (pem->end < len && in[pem->end] != '\n') {
+               pem->end++;
+       }
+#if 0
+       fprintf(stderr, "found cert:\n");
+       write(2, in+pem->start, pem->len);
+       write(2, in+pem->start+pem->len, pem->end-pem->start-pem->len+1);
+#endif
+
+       return in + pem->start;
+}
+#endif
+
+unsigned char *uudecode(const unsigned char *in, size_t len, size_t *outlen) {
+       size_t need = len/4 * 3 + 1;
+       unsigned char *out;
+
+       out = malloc(need);
+       if (out) {
+               base64decode((const char *)in, len, out, &need);
+               if (outlen) {
+                       *outlen = need;
+               }
+       }
+       return out;
+}
+
+unsigned char *tls_pem_decode(const unsigned char *data_in,
+                             unsigned int input_length, int cert_index,
+                             unsigned int *output_len) {
+       unsigned int i;
+       size_t alloc_len = input_length / 4 * 3;
+       unsigned char *output = malloc(alloc_len);
+       *output_len = 0;
+       if (!output)
+               return NULL;
+       unsigned int start_at = 0;
+       unsigned int idx = 0;
+
+       for (i = 0; i < input_length; i++) {
+               if ((data_in[i] == '\n') || (data_in[i] == '\r'))
+                       continue;
+
+               if (data_in[i] != '-') {
+                       /* read entire line */
+                       while ((i < input_length) && (data_in[i] != '\n'))
+                               i++;
+                       continue;
+               }
+
+               if (data_in[i] == '-') {
+                       unsigned int end_idx = i;
+                       /* read until end of line */
+                       while ((i < input_length) && (data_in[i] != '\n'))
+                               i++;
+                       if (start_at) {
+                               if (cert_index > 0) {
+                                       cert_index--;
+                                       start_at = 0;
+                               } else {
+                                           base64decode((const char
+                                                                *)
+                                                               &data_in
+                                                               [start_at],
+                                                               end_idx -
+                                                               start_at,
+                                                               output,
+                                                               &alloc_len);
+                                           idx = alloc_len;
+                                       break;
+                               }
+                       } else
+                               start_at = i + 1;
+               }
+       }
+       *output_len = idx;
+       if (!idx) {
+               free(output);
+               return NULL;
+       }
+       return output;
+}
+
+#if 0
+int load_cert_list(struct TLSContext *tls, const unsigned char *buf, size_t
+               bufsize, struct TLSCertificate **certs, int flags) {
+       struct pem_loc loc = { 0 };
+       struct TLSCertificate *cert = 0;
+       const unsigned char *pem = buf;
+       int count = 0;
+
+       while (bufsize > 0) {
+               if (bufsize <= loc.end) {
+                       break;
+               }
+               bufsize -= loc.end;
+
+               //fprintf(stderr, "checking %zu bytes at %p\n", remaining, pem);
+               pem = next_pem(pem, bufsize, &loc);
+               if (!pem) {
+                       break;
+               }
+               cert = asn1_parse(tls, buf+loc.start, loc.len, 0);
+               if (!cert) {
+                       continue;
+               }
+#ifdef TLS_X509_V1_SUPPORT
+               if (cert->version != 2 && cert->version != 0)
+#else
+               if (cert->version != 2)
+#endif
+               {
+                       free(cert);
+                       cert = 0;
+               }
+               count++;
+
+               //fprintf(stderr, "found length %zu pem starting at %zu taking %zu bytes\n",
+                               //loc.len,
+                               //loc.start,
+                               //loc.end);
+       }
+       return 0;
+}
+#endif
+
+int add_to_list(struct TLSCertificate ***list, int *len,
+               struct TLSCertificate *cert) {
+       void *new;
+
+       new = TLS_REALLOC(*list, (*len+1) * sizeof *list);
+       if (new) {
+               *list = new;
+               (*list)[(*len)++] = cert;
+       } else {
+               return 0;
+       }
+       return *len;
+}
+
+int tls_load_certificates(struct TLSContext *tls,
+                         const unsigned char *pem, int size) {
+       if (!tls)
+               return TLS_GENERIC_ERROR;
+
+       unsigned int len;
+       int idx = 0;
+       do {
+               unsigned char *data =
+                   tls_pem_decode(pem, size, idx++, &len);
+               if ((!data) || (!len))
+                       break;
+               struct TLSCertificate *cert =
+                   asn1_parse(tls, data, len, 0);
+               if (cert) {
+                       if ((cert->version == 2)
+#ifdef TLS_X509_V1_SUPPORT
+                           || (cert->version == 0)
+#endif
+                           ) {
+
+                               free(cert->der_bytes);
+                               cert->der_bytes = data;
+                               cert->der_len = len;
+                               data = NULL;
+                               if (cert->priv) {
+                                       DEBUG_PRINT
+                                           ("WARNING - parse error (private key encountered in certificate)\n");
+                                       free(cert->priv);
+                                       cert->priv = NULL;
+                                       cert->priv_len = 0;
+                               }
+                               add_to_list(&tls->certificates, &tls->certificates_count, cert);
+
+                       } else {
+                               DEBUG_PRINT
+                                   ("WARNING - certificate version error (v%d)\n",
+                                    (int) cert->version);
+                               tls_destroy_certificate(cert);
+                       }
+               }
+               free(data);
+       } while (1);
+       return tls->certificates_count;
+}
+
+int tls_load_private_key(struct TLSContext *tls,
+                        const unsigned char *pem_buffer, int pem_size) {
+       if (!tls)
+               return TLS_GENERIC_ERROR;
+
+       unsigned int len;
+       int idx = 0;
+       do {
+               unsigned char *data =
+                   tls_pem_decode(pem_buffer, pem_size, idx++, &len);
+               if ((!data) || (!len))
+                       break;
+               struct TLSCertificate *cert =
+                   asn1_parse(tls, data, len, -1);
+               if (cert) {
+                       if (!cert->der_len) {
+                               free(cert->der_bytes);
+                               cert->der_bytes = data;
+                               cert->der_len = len;
+                       } else {
+                               free(data);
+                       }
+                       if ((cert) && (cert->priv) && (cert->priv_len)) {
+#ifdef TLS_ECDSA_SUPPORTED
+                               if (cert->ec_algorithm) {
+                                       DEBUG_PRINT
+                                           ("Loaded ECC private key\n");
+                                       if (tls->ec_private_key)
+                                               tls_destroy_certificate
+                                                   (tls->ec_private_key);
+                                       tls->ec_private_key = cert;
+                                       return 1;
+                               } else
+#endif
+                               {
+                                       DEBUG_PRINT
+                                           ("Loaded private key\n");
+                                       if (tls->private_key)
+                                               tls_destroy_certificate
+                                                   (tls->private_key);
+                                       tls->private_key = cert;
+                                       return 1;
+                               }
+                       }
+                       tls_destroy_certificate(cert);
+               } else
+                       free(data);
+       } while (1);
+       return 0;
+}
+
+int tls_load_root_certificates(struct TLSContext *tls,
+                              const unsigned char *pem_buffer,
+                              int pem_size) {
+       if (!tls)
+               return -1;
+
+       unsigned int len;
+       int idx = 0;
+
+       do {
+               unsigned char *data =
+                   tls_pem_decode(pem_buffer, pem_size, idx++, &len);
+               if ((!data) || (!len))
+                       break;
+               struct TLSCertificate *cert =
+                   asn1_parse(NULL, data, len, 0);
+               if (cert) {
+                       if (cert->version == 2) {
+                               if (cert->priv) {
+                                       DEBUG_PRINT
+                                           ("WARNING - parse error (private key encountered in certificate)\n");
+                                       free(cert->priv);
+                                       cert->priv = NULL;
+                                       cert->priv_len = 0;
+                               }
+                               add_to_list(&tls->root_certificates,
+                                               &tls->root_count,
+                                               cert);
+                               DEBUG_PRINT("Loaded certificate: %d\n",
+                                           tls->root_count);
+                       } else {
+                               DEBUG_PRINT
+                                   ("WARNING - certificate version error (v%d)\n",
+                                    (int) cert->version);
+                               tls_destroy_certificate(cert);
+                       }
+               }
+               free(data);
+       } while (1);
+       return tls->root_count;
+}
+