tests: add test cases for blob parsing
authorPetr Štetiar <ynezz@true.cz>
Mon, 9 Dec 2019 12:28:25 +0000 (13:28 +0100)
committerPetr Štetiar <ynezz@true.cz>
Wed, 25 Dec 2019 09:31:58 +0000 (10:31 +0100)
Increasing test coverage.

Signed-off-by: Petr Štetiar <ynezz@true.cz>
tests/cram/inputs/invalid.ucert [new file with mode: 0644]
tests/cram/inputs/key-build.ucert [new file with mode: 0644]
tests/cram/test_blob_parse.t [new file with mode: 0644]
tests/test-blob-parse.c [new file with mode: 0644]

diff --git a/tests/cram/inputs/invalid.ucert b/tests/cram/inputs/invalid.ucert
new file mode 100644 (file)
index 0000000..dbdeb72
Binary files /dev/null and b/tests/cram/inputs/invalid.ucert differ
diff --git a/tests/cram/inputs/key-build.ucert b/tests/cram/inputs/key-build.ucert
new file mode 100644 (file)
index 0000000..8b347b1
Binary files /dev/null and b/tests/cram/inputs/key-build.ucert differ
diff --git a/tests/cram/test_blob_parse.t b/tests/cram/test_blob_parse.t
new file mode 100644 (file)
index 0000000..77d8bdd
--- /dev/null
@@ -0,0 +1,76 @@
+check that blob_parse is producing expected results:
+
+  $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH"
+  $ export TEST_INPUTS="$TESTDIR/inputs"
+  $ export FUZZ_CORPUS="$TESTDIR/../fuzz/corpus"
+
+  $ valgrind --quiet --leak-check=full test-blob-parse $TEST_INPUTS/key-build.ucert
+  === CHAIN ELEMENT 01 ===
+  signature:
+  ---
+  untrusted comment: signed by key 84bfc88a17166577
+  RWSEv8iKFxZld+bQ+NTqCdDlHOuVYNw5Qw7Q8shjfMgFJcTqrzaqO0bysjIQhTadmcwvWiWvHlyMcwAXSix2BYdfghz/zhDjvgU=
+  ---
+  payload:
+  ---
+  "ucert": {
+  \t"certtype": 1, (esc)
+  \t"validfrom": 1546188410, (esc)
+  \t"expiresat": 1577724410, (esc)
+  \t"pubkey": "untrusted comment: Local build key\\nRWSEv8iKFxZld6vicE1icWhYNfEV9PM7C9MKUKl+YNEKB+PdAWGDF5Z9\\n" (esc)
+  }
+  ---
+
+  $ valgrind --quiet --leak-check=full test-blob-parse $TEST_INPUTS/invalid.ucert
+  cannot parse cert invalid.ucert
+
+  $ test-blob-parse-san $TEST_INPUTS/key-build.ucert
+  === CHAIN ELEMENT 01 ===
+  signature:
+  ---
+  untrusted comment: signed by key 84bfc88a17166577
+  RWSEv8iKFxZld+bQ+NTqCdDlHOuVYNw5Qw7Q8shjfMgFJcTqrzaqO0bysjIQhTadmcwvWiWvHlyMcwAXSix2BYdfghz/zhDjvgU=
+  ---
+  payload:
+  ---
+  "ucert": {
+  \t"certtype": 1, (esc)
+  \t"validfrom": 1546188410, (esc)
+  \t"expiresat": 1577724410, (esc)
+  \t"pubkey": "untrusted comment: Local build key\\nRWSEv8iKFxZld6vicE1icWhYNfEV9PM7C9MKUKl+YNEKB+PdAWGDF5Z9\\n" (esc)
+  }
+  ---
+
+  $ test-blob-parse-san $TEST_INPUTS/invalid.ucert
+  cannot parse cert invalid.ucert
+
+  $ for blob in $(LC_ALL=C find $FUZZ_CORPUS -type f | sort ); do
+  >   valgrind --quiet --leak-check=full test-blob-parse $blob; \
+  >   test-blob-parse-san $blob; \
+  > done
+  cannot parse cert 71520a5c4b5ca73903216857abbad54a8002d44a
+  cannot parse cert 71520a5c4b5ca73903216857abbad54a8002d44a
+  cannot parse cert c1dfd96eea8cc2b62785275bca38ac261256e278
+  cannot parse cert c1dfd96eea8cc2b62785275bca38ac261256e278
+  cannot parse cert c42ac1c46f1d4e211c735cc7dfad4ff8391110e9
+  cannot parse cert c42ac1c46f1d4e211c735cc7dfad4ff8391110e9
+  cannot parse cert crash-1b8fb1be45db3aff7699100f497fb74138f3df4f
+  cannot parse cert crash-1b8fb1be45db3aff7699100f497fb74138f3df4f
+  cannot parse cert crash-4c4d2c3c9ade5da9347534e290305c3b9760f627
+  cannot parse cert crash-4c4d2c3c9ade5da9347534e290305c3b9760f627
+  cannot parse cert crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b
+  cannot parse cert crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b
+  cannot parse cert crash-75b146c4e6fac64d3e62236b27c64b50657bab2a
+  cannot parse cert crash-75b146c4e6fac64d3e62236b27c64b50657bab2a
+  cannot parse cert crash-813f3e68661da09c26d4a87dbb9d5099e92be50f
+  cannot parse cert crash-813f3e68661da09c26d4a87dbb9d5099e92be50f
+  cannot parse cert crash-98595faa58ba01d85ba4fd0b109cd3d490b45795
+  cannot parse cert crash-98595faa58ba01d85ba4fd0b109cd3d490b45795
+  cannot parse cert crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1
+  cannot parse cert crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1
+  cannot parse cert crash-df9d1243057b27bbad6211e5a23d1cb699028aa2
+  cannot parse cert crash-df9d1243057b27bbad6211e5a23d1cb699028aa2
+  cannot parse cert crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2
+  cannot parse cert crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2
+  cannot parse cert valid-blobmsg.bin
+  cannot parse cert valid-blobmsg.bin
diff --git a/tests/test-blob-parse.c b/tests/test-blob-parse.c
new file mode 100644 (file)
index 0000000..6b1fb56
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Based on certificate dump functionality from ucert.c:
+ *
+ *  Copyright (C) 2018 Daniel Golle <daniel@makrotopia.org>
+ *  SPDX-License-Identifier: GPL-3.0
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <libgen.h>
+
+#include "blob.h"
+#include "list.h"
+#include "blobmsg_json.h"
+
+#define CERT_BUF_LEN 4096
+
+/*
+ * ucert structure
+ * |               BLOB                    |
+ * |    SIGNATURE    |       PAYLOAD       |
+ * |                 |[ BLOBMSG CONTAINER ]|
+ * |                 |[[T,i,v,e,f,pubkey ]]|
+ */
+enum cert_attr {
+       CERT_ATTR_SIGNATURE,
+       CERT_ATTR_PAYLOAD,
+       CERT_ATTR_MAX
+};
+
+static const struct blob_attr_info cert_policy[CERT_ATTR_MAX] = {
+       [CERT_ATTR_SIGNATURE] = { .type = BLOB_ATTR_BINARY },
+       [CERT_ATTR_PAYLOAD] = { .type = BLOB_ATTR_NESTED },
+};
+
+enum cert_cont_attr {
+       CERT_CT_ATTR_PAYLOAD,
+       CERT_CT_ATTR_MAX
+};
+
+enum cert_payload_attr {
+       CERT_PL_ATTR_CERTTYPE,
+       CERT_PL_ATTR_CERTID,
+       CERT_PL_ATTR_VALIDFROMTIME,
+       CERT_PL_ATTR_EXPIRETIME,
+       CERT_PL_ATTR_PUBKEY,
+       CERT_PL_ATTR_KEY_FINGERPRINT,
+       CERT_PL_ATTR_MAX
+};
+
+enum certtype_id {
+       CERTTYPE_UNSPEC,
+       CERTTYPE_AUTH,
+       CERTTYPE_REVOKE
+};
+
+/* list to store certificate chain at runtime */
+struct cert_object {
+       struct list_head list;
+       struct blob_attr *cert[CERT_ATTR_MAX];
+};
+
+static int cert_load(const char *certfile, struct list_head *chain)
+{
+       FILE *f;
+       struct blob_attr *certtb[CERT_ATTR_MAX];
+       struct blob_attr *bufpt;
+       struct cert_object *cobj;
+       char filebuf[CERT_BUF_LEN];
+       int ret = 0, pret = 0;
+       size_t len, pos = 0;
+
+       f = fopen(certfile, "r");
+       if (!f)
+               return 1;
+
+       len = fread(&filebuf, 1, CERT_BUF_LEN - 1, f);
+       if (len < 64)
+               return 1;
+
+       ret = ferror(f) || !feof(f);
+       fclose(f);
+       if (ret)
+               return 1;
+
+       bufpt = (struct blob_attr *)filebuf;
+       do {
+               pret = blob_parse(bufpt, certtb, cert_policy, CERT_ATTR_MAX);
+               if (pret <= 0)
+                       /* no attributes found */
+                       break;
+
+               if (pos + blob_pad_len(bufpt) > len)
+                       /* blob exceeds filebuffer */
+                       break;
+               else
+                       pos += blob_pad_len(bufpt);
+
+               if (!certtb[CERT_ATTR_SIGNATURE])
+                       /* no signature -> drop */
+                       break;
+
+               cobj = calloc(1, sizeof(*cobj));
+               cobj->cert[CERT_ATTR_SIGNATURE] = blob_memdup(certtb[CERT_ATTR_SIGNATURE]);
+               if (certtb[CERT_ATTR_PAYLOAD])
+                       cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]);
+
+               list_add_tail(&cobj->list, chain);
+               ret += pret;
+       /* repeat parsing while there is still enough remaining data in buffer */
+       } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt)));
+
+       return (ret <= 0);
+}
+
+/* dump single chain element to console */
+static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX])
+{
+       int i;
+       char *json = NULL;
+
+       for (i = 0; i < CERT_ATTR_MAX; i++) {
+               struct blob_attr *v = cert[i];
+
+               if (!v)
+                       continue;
+
+               switch(cert_policy[i].type) {
+               case BLOB_ATTR_BINARY:
+                       fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v));
+                       break;
+               case BLOB_ATTR_NESTED:
+                       json = blobmsg_format_json_indent(blob_data(v), false, 0);
+                       if (!json)
+                               continue;
+
+                       fprintf(stdout, "payload:\n---\n%s\n---\n", json);
+                       free(json);
+                       break;
+               }
+       }
+}
+
+static int cert_dump(const char *certfile)
+{
+       struct cert_object *cobj;
+       static LIST_HEAD(certchain);
+       unsigned int count = 0;
+
+       if (cert_load(certfile, &certchain)) {
+               fprintf(stderr, "cannot parse cert %s\n", basename((char *) certfile));
+               return 1;
+       }
+
+       list_for_each_entry(cobj, &certchain, list) {
+               fprintf(stdout, "=== CHAIN ELEMENT %02u ===\n", ++count);
+               cert_dump_blob(cobj->cert);
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2) {
+               fprintf(stderr, "Usage: %s <cert.ucert>\n", argv[0]);
+               return 3;
+       }
+
+       cert_dump(argv[1]);
+
+       return 0;
+}