diff options
Diffstat (limited to 'test/monniaux/BearSSL/tools/files.c')
-rw-r--r-- | test/monniaux/BearSSL/tools/files.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/test/monniaux/BearSSL/tools/files.c b/test/monniaux/BearSSL/tools/files.c new file mode 100644 index 00000000..8bf67cc3 --- /dev/null +++ b/test/monniaux/BearSSL/tools/files.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + +#include "brssl.h" + +/* see brssl.h */ +unsigned char * +read_file(const char *fname, size_t *len) +{ + bvector vbuf = VEC_INIT; + FILE *f; + + *len = 0; + f = fopen(fname, "rb"); + if (f == NULL) { + fprintf(stderr, + "ERROR: could not open file '%s' for reading\n", fname); + return NULL; + } + for (;;) { + unsigned char tmp[1024]; + size_t rlen; + + rlen = fread(tmp, 1, sizeof tmp, f); + if (rlen == 0) { + unsigned char *buf; + + if (ferror(f)) { + fprintf(stderr, + "ERROR: read error on file '%s'\n", + fname); + fclose(f); + return NULL; + } + buf = VEC_TOARRAY(vbuf); + *len = VEC_LEN(vbuf); + VEC_CLEAR(vbuf); + fclose(f); + return buf; + } + VEC_ADDMANY(vbuf, tmp, rlen); + } +} + +/* see brssl.h */ +int +write_file(const char *fname, const void *data, size_t len) +{ + FILE *f; + const unsigned char *buf; + + f = fopen(fname, "wb"); + if (f == NULL) { + fprintf(stderr, + "ERROR: could not open file '%s' for reading\n", fname); + return -1; + } + buf = data; + while (len > 0) { + size_t wlen; + + wlen = fwrite(buf, 1, len, f); + if (wlen == 0) { + fprintf(stderr, + "ERROR: could not write all bytes to '%s'\n", + fname); + fclose(f); + return -1; + } + buf += wlen; + len -= wlen; + } + if (ferror(f)) { + fprintf(stderr, "ERROR: write error on file '%s'\n", fname); + fclose(f); + return -1; + } + fclose(f); + return 0; +} + +/* see brssl.h */ +int +looks_like_DER(const unsigned char *buf, size_t len) +{ + int fb; + size_t dlen; + + if (len < 2) { + return 0; + } + if (*buf ++ != 0x30) { + return 0; + } + fb = *buf ++; + len -= 2; + if (fb < 0x80) { + return (size_t)fb == len; + } else if (fb == 0x80) { + return 0; + } else { + fb -= 0x80; + if (len < (size_t)fb + 2) { + return 0; + } + len -= (size_t)fb; + dlen = 0; + while (fb -- > 0) { + if (dlen > (len >> 8)) { + return 0; + } + dlen = (dlen << 8) + (size_t)*buf ++; + } + return dlen == len; + } +} + +static void +vblob_append(void *cc, const void *data, size_t len) +{ + bvector *bv; + + bv = cc; + VEC_ADDMANY(*bv, data, len); +} + +/* see brssl.h */ +void +free_pem_object_contents(pem_object *po) +{ + if (po != NULL) { + xfree(po->name); + xfree(po->data); + } +} + +/* see brssl.h */ +pem_object * +decode_pem(const void *src, size_t len, size_t *num) +{ + VECTOR(pem_object) pem_list = VEC_INIT; + br_pem_decoder_context pc; + pem_object po, *pos; + const unsigned char *buf; + bvector bv = VEC_INIT; + int inobj; + int extra_nl; + + *num = 0; + br_pem_decoder_init(&pc); + buf = src; + inobj = 0; + po.name = NULL; + po.data = NULL; + po.data_len = 0; + extra_nl = 1; + while (len > 0) { + size_t tlen; + + tlen = br_pem_decoder_push(&pc, buf, len); + buf += tlen; + len -= tlen; + switch (br_pem_decoder_event(&pc)) { + + case BR_PEM_BEGIN_OBJ: + po.name = xstrdup(br_pem_decoder_name(&pc)); + br_pem_decoder_setdest(&pc, vblob_append, &bv); + inobj = 1; + break; + + case BR_PEM_END_OBJ: + if (inobj) { + po.data = VEC_TOARRAY(bv); + po.data_len = VEC_LEN(bv); + VEC_ADD(pem_list, po); + VEC_CLEAR(bv); + po.name = NULL; + po.data = NULL; + po.data_len = 0; + inobj = 0; + } + break; + + case BR_PEM_ERROR: + xfree(po.name); + VEC_CLEAR(bv); + fprintf(stderr, + "ERROR: invalid PEM encoding\n"); + VEC_CLEAREXT(pem_list, &free_pem_object_contents); + return NULL; + } + + /* + * We add an extra newline at the end, in order to + * support PEM files that lack the newline on their last + * line (this is somwehat invalid, but PEM format is not + * standardised and such files do exist in the wild, so + * we'd better accept them). + */ + if (len == 0 && extra_nl) { + extra_nl = 0; + buf = (const unsigned char *)"\n"; + len = 1; + } + } + if (inobj) { + fprintf(stderr, "ERROR: unfinished PEM object\n"); + xfree(po.name); + VEC_CLEAR(bv); + VEC_CLEAREXT(pem_list, &free_pem_object_contents); + return NULL; + } + + *num = VEC_LEN(pem_list); + VEC_ADD(pem_list, po); + pos = VEC_TOARRAY(pem_list); + VEC_CLEAR(pem_list); + return pos; +} + +/* see brssl.h */ +br_x509_certificate * +read_certificates(const char *fname, size_t *num) +{ + VECTOR(br_x509_certificate) cert_list = VEC_INIT; + unsigned char *buf; + size_t len; + pem_object *pos; + size_t u, num_pos; + br_x509_certificate *xcs; + br_x509_certificate dummy; + + *num = 0; + + /* + * TODO: reading the whole file is crude; we could parse them + * in a streamed fashion. But it does not matter much in practice. + */ + buf = read_file(fname, &len); + if (buf == NULL) { + return NULL; + } + + /* + * Check for a DER-encoded certificate. + */ + if (looks_like_DER(buf, len)) { + xcs = xmalloc(2 * sizeof *xcs); + xcs[0].data = buf; + xcs[0].data_len = len; + xcs[1].data = NULL; + xcs[1].data_len = 0; + *num = 1; + return xcs; + } + + pos = decode_pem(buf, len, &num_pos); + xfree(buf); + if (pos == NULL) { + return NULL; + } + for (u = 0; u < num_pos; u ++) { + if (eqstr(pos[u].name, "CERTIFICATE") + || eqstr(pos[u].name, "X509 CERTIFICATE")) + { + br_x509_certificate xc; + + xc.data = pos[u].data; + xc.data_len = pos[u].data_len; + pos[u].data = NULL; + VEC_ADD(cert_list, xc); + } + } + for (u = 0; u < num_pos; u ++) { + free_pem_object_contents(&pos[u]); + } + xfree(pos); + + if (VEC_LEN(cert_list) == 0) { + fprintf(stderr, "ERROR: no certificate in file '%s'\n", fname); + return NULL; + } + *num = VEC_LEN(cert_list); + dummy.data = NULL; + dummy.data_len = 0; + VEC_ADD(cert_list, dummy); + xcs = VEC_TOARRAY(cert_list); + VEC_CLEAR(cert_list); + return xcs; +} + +/* see brssl.h */ +void +free_certificates(br_x509_certificate *certs, size_t num) +{ + size_t u; + + for (u = 0; u < num; u ++) { + xfree(certs[u].data); + } + xfree(certs); +} |