]> www.infradead.org Git - mtd-utils.git/commitdiff
mkfs.ubifs: Add authentication support
authorSascha Hauer <s.hauer@pengutronix.de>
Tue, 6 Aug 2019 10:49:28 +0000 (12:49 +0200)
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Mon, 19 Aug 2019 07:27:00 +0000 (09:27 +0200)
This adds support for authenticated UBIFS images. In authenticated
images all UBIFS nodes are hashed as described in the UBIFS
authentication whitepaper. Additionally the superblock node contains a
hash of the master node and itself is cryptographically signed in a node
following the superblock node. The signature is in PKCS #7 CMS format.

To generate an authenticated image these options are necessary:

--hash-algo=NAME     hash algorithm to use for signed images
                     (Valid options include sha1, sha256, sha512)
--auth-key=FILE      filename or PKCS #11 uri containing the authentication key
                     for signing
--auth-cert=FILE     Authentication certificate filename for signing. Unused
                     when certificate is provided via PKCS #11

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
ubifs-utils/Makemodule.am
ubifs-utils/mkfs.ubifs/lpt.c
ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
ubifs-utils/mkfs.ubifs/mkfs.ubifs.h
ubifs-utils/mkfs.ubifs/sign.c [new file with mode: 0644]
ubifs-utils/mkfs.ubifs/sign.h [new file with mode: 0644]
ubifs-utils/mkfs.ubifs/ubifs.h

index 164ce09cef5860eba7e54c31fbd38fa37d679d23..0c64445617b3b2c25e11f90d6d9a67f8f1940c93 100644 (file)
@@ -19,7 +19,8 @@ mkfs_ubifs_SOURCES = \
 
 if WITH_CRYPTO
 mkfs_ubifs_SOURCES += ubifs-utils/mkfs.ubifs/crypto.c \
-               ubifs-utils/mkfs.ubifs/fscrypt.c
+               ubifs-utils/mkfs.ubifs/fscrypt.c \
+               ubifs-utils/mkfs.ubifs/sign.c
 endif
 
 mkfs_ubifs_LDADD = libmtd.a libubi.a $(ZLIB_LIBS) $(LZO_LIBS) $(ZSTD_LIBS) $(UUID_LIBS) $(LIBSELINUX_LIBS) $(OPENSSL_LIBS) -lm
index 6aa0b882b88c2304ab8830e19e007b59007262f9..7ee739a96fafb9361f9bde600c6b624478612b58 100644 (file)
 
 #include "mkfs.ubifs.h"
 
+#ifdef WITH_CRYPTO
+#include <openssl/evp.h>
+#endif
+
 /**
  * do_calc_lpt_geom - calculate sizes for the LPT area.
  * @c: the UBIFS file-system description object
@@ -374,6 +378,7 @@ int create_lpt(struct ubifs_info *c)
        struct ubifs_nnode *nnode = NULL;
        void *buf = NULL, *p;
        int *lsave = NULL;
+       unsigned int md_len;
 
        pnode = malloc(sizeof(struct ubifs_pnode));
        nnode = malloc(sizeof(struct ubifs_nnode));
@@ -386,6 +391,8 @@ int create_lpt(struct ubifs_info *c)
        memset(pnode, 0 , sizeof(struct ubifs_pnode));
        memset(nnode, 0 , sizeof(struct ubifs_nnode));
 
+       hash_digest_init();
+
        c->lscan_lnum = c->main_first;
 
        lnum = c->lpt_first;
@@ -429,6 +436,9 @@ int create_lpt(struct ubifs_info *c)
                        }
                }
                pack_pnode(c, p, pnode);
+
+               hash_digest_update(p, c->pnode_sz);
+
                p += c->pnode_sz;
                len += c->pnode_sz;
                /*
@@ -439,6 +449,8 @@ int create_lpt(struct ubifs_info *c)
                pnode->num += 1;
        }
 
+       hash_digest_final(c->lpt_hash, &md_len);
+
        row = c->lpt_hght - 1;
        /* Add all nnodes, one level at a time */
        while (1) {
index 58ebf8ce9d48793f6e260ac6b2ace66de0b0f2b4..5748aaac0461fb716acdc3d3759a3e3df7088d3a 100644 (file)
@@ -68,6 +68,7 @@ static char *secontext;
  * @lnum: LEB number
  * @offs: offset
  * @len: length
+ * @hash: hash of the node
  *
  * The index is recorded as a linked list which is sorted and used to create
  * the bottom level of the on-flash index tree. The remaining levels of the
@@ -82,6 +83,7 @@ struct idx_entry {
        int lnum;
        int offs;
        int len;
+       uint8_t hash[UBIFS_MAX_HASH_LEN];
 };
 
 /**
@@ -164,6 +166,12 @@ static unsigned long long creat_sqnum;
 
 static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQqaK:b:P:C:";
 
+enum {
+       HASH_ALGO_OPTION = CHAR_MAX + 1,
+       AUTH_KEY_OPTION,
+       AUTH_CERT_OPTION,
+};
+
 static const struct option longopts[] = {
        {"root",               1, NULL, 'r'},
        {"min-io-size",        1, NULL, 'm'},
@@ -192,6 +200,9 @@ static const struct option longopts[] = {
        {"key-descriptor",     1, NULL, 'b'},
        {"padding",            1, NULL, 'P'},
        {"cipher",             1, NULL, 'C'},
+       {"hash-algo",          1, NULL, HASH_ALGO_OPTION},
+       {"auth-key",           1, NULL, AUTH_KEY_OPTION},
+       {"auth-cert",          1, NULL, AUTH_CERT_OPTION},
        {NULL, 0, NULL, 0}
 };
 
@@ -242,6 +253,12 @@ static const char *helptext =
 "                         (default = 4).\n"
 "-C, --cipher=NAME        Specify cipher to use for file level encryption\n"
 "                         (default is \"AES-256-XTS\").\n"
+"    --hash-algo=NAME     hash algorithm to use for signed images\n"
+"                         (Valid options include sha1, sha256, sha512)\n"
+"    --auth-key=FILE      filename or PKCS #11 uri containing the authentication key\n"
+"                         for signing\n"
+"    --auth-cert=FILE     Authentication certificate filename for signing. Unused\n"
+"                         when certificate is provided via PKCS #11\n"
 "-h, --help               display this help text\n\n"
 "Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n"
 "Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n"
@@ -261,7 +278,15 @@ static const char *helptext =
 "when flashing the image and the second time when UBIFS is mounted and writes useful\n"
 "data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n"
 "flag may make the first mount very slow, because the \"free space fixup\" procedure\n"
-"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n";
+"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"
+"\n"
+"mkfs.ubifs supports building signed images. For this the \"--hash-algo\",\n"
+"\"--auth-key\" and \"--auth-cert\" options have to be specified.\n";
+
+static inline uint8_t *ubifs_branch_hash(struct ubifs_branch *br)
+{
+       return (void *)br + sizeof(*br) + c->key_len;
+}
 
 /**
  * make_path - make a path name from a directory and a name.
@@ -753,14 +778,27 @@ static int get_options(int argc, char**argv)
                        }
                        break;
                }
-               case 'C':
 #ifdef WITH_CRYPTO
+               case 'C':
                        cipher_name = optarg;
+                       break;
+               case HASH_ALGO_OPTION:
+                       c->hash_algo_name = xstrdup(optarg);
+                       break;
+               case AUTH_KEY_OPTION:
+                       c->auth_key_filename = xstrdup(optarg);
+                       break;
+               case AUTH_CERT_OPTION:
+                       c->auth_cert_filename = xstrdup(optarg);
+                       break;
+               }
 #else
+               case 'C':
+               case HASH_ALGO_OPTION:
+               case AUTH_KEY_OPTION:
+               case X509_OPTION:
                        return err_msg("mkfs.ubifs was built without crypto support.");
 #endif
-                       break;
-               }
        }
 
        if (optind != argc && !output)
@@ -1063,9 +1101,10 @@ static void set_lprops(int lnum, int offs, int flags)
  * @lnum: node LEB number
  * @offs: node offset
  * @len: node length
+ * @hash: hash of the node
  */
 static int add_to_index(union ubifs_key *key, char *name, int name_len,
-                       int lnum, int offs, int len)
+                       int lnum, int offs, int len, const uint8_t *hash)
 {
        struct idx_entry *e;
 
@@ -1079,6 +1118,8 @@ static int add_to_index(union ubifs_key *key, char *name, int name_len,
        e->lnum = lnum;
        e->offs = offs;
        e->len = len;
+       memcpy(e->hash, hash, c->hash_len);
+
        if (!idx_list_first)
                idx_list_first = e;
        if (idx_list_last)
@@ -1137,6 +1178,7 @@ static int reserve_space(int len, int *lnum, int *offs)
 static int add_node(union ubifs_key *key, char *name, int name_len, void *node, int len)
 {
        int err, lnum, offs, type = key_type(key);
+       uint8_t hash[UBIFS_MAX_HASH_LEN];
 
        if (type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY) {
                if (!name)
@@ -1156,7 +1198,9 @@ static int add_node(union ubifs_key *key, char *name, int name_len, void *node,
        memcpy(leb_buf + offs, node, len);
        memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len);
 
-       add_to_index(key, name, name_len, lnum, offs, len);
+       ubifs_node_calc_hash(node, hash);
+
+       add_to_index(key, name, name_len, lnum, offs, len, hash);
 
        return 0;
 }
@@ -2298,6 +2342,7 @@ static int write_index(void)
        struct ubifs_idx_node *idx;
        struct ubifs_branch *br;
        int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err;
+       uint8_t *hashes;
 
        dbg_msg(1, "leaf node count: %zd", idx_cnt);
 
@@ -2321,6 +2366,9 @@ static int write_index(void)
        cnt = idx_cnt / c->fanout;
        if (idx_cnt % c->fanout)
                cnt += 1;
+
+       hashes = xmalloc(c->hash_len * cnt);
+
        p = idx_ptr;
        blnum = head_lnum;
        boffs = head_offs;
@@ -2345,8 +2393,11 @@ static int write_index(void)
                        br->lnum = cpu_to_le32((*p)->lnum);
                        br->offs = cpu_to_le32((*p)->offs);
                        br->len = cpu_to_le32((*p)->len);
+                       memcpy(ubifs_branch_hash(br), (*p)->hash, c->hash_len);
                }
                add_idx_node(idx, child_cnt);
+
+               ubifs_node_calc_hash(idx, hashes + i * c->hash_len);
        }
        /* Write level 1 index nodes and above */
        level = 0;
@@ -2423,11 +2474,18 @@ static int write_index(void)
                                 */
                                boffs += ALIGN(blen, 8);
                                p += pstep;
+
+                               memcpy(ubifs_branch_hash(br),
+                                      hashes + bn * c->hash_len,
+                                      c->hash_len);
                        }
                        add_idx_node(idx, child_cnt);
+                       ubifs_node_calc_hash(idx, hashes + i * c->hash_len);
                }
        }
 
+       memcpy(c->root_idx_hash, hashes, c->hash_len);
+
        /* Free stuff */
        for (i = 0; i < idx_cnt; i++) {
                free(idx_ptr[i]->name);
@@ -2512,44 +2570,75 @@ static int ubifs_format_version(void)
  */
 static int write_super(void)
 {
-       struct ubifs_sb_node sup;
-
-       memset(&sup, 0, UBIFS_SB_NODE_SZ);
-
-       sup.ch.node_type  = UBIFS_SB_NODE;
-       sup.key_hash      = c->key_hash_type;
-       sup.min_io_size   = cpu_to_le32(c->min_io_size);
-       sup.leb_size      = cpu_to_le32(c->leb_size);
-       sup.leb_cnt       = cpu_to_le32(c->leb_cnt);
-       sup.max_leb_cnt   = cpu_to_le32(c->max_leb_cnt);
-       sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes);
-       sup.log_lebs      = cpu_to_le32(c->log_lebs);
-       sup.lpt_lebs      = cpu_to_le32(c->lpt_lebs);
-       sup.orph_lebs     = cpu_to_le32(c->orph_lebs);
-       sup.jhead_cnt     = cpu_to_le32(c->jhead_cnt);
-       sup.fanout        = cpu_to_le32(c->fanout);
-       sup.lsave_cnt     = cpu_to_le32(c->lsave_cnt);
-       sup.fmt_version   = cpu_to_le32(ubifs_format_version());
-       sup.default_compr = cpu_to_le16(c->default_compr);
-       sup.rp_size       = cpu_to_le64(c->rp_size);
-       sup.time_gran     = cpu_to_le32(DEFAULT_TIME_GRAN);
-       uuid_generate_random(sup.uuid);
+       void *buf;
+       struct ubifs_sb_node *sup;
+       struct ubifs_sig_node *sig;
+       int err, len;
+
+       buf = xzalloc(c->leb_size);
+
+       sup = buf;
+       sig = buf + UBIFS_SB_NODE_SZ;
+
+       sup->ch.node_type  = UBIFS_SB_NODE;
+       sup->key_hash      = c->key_hash_type;
+       sup->min_io_size   = cpu_to_le32(c->min_io_size);
+       sup->leb_size      = cpu_to_le32(c->leb_size);
+       sup->leb_cnt       = cpu_to_le32(c->leb_cnt);
+       sup->max_leb_cnt   = cpu_to_le32(c->max_leb_cnt);
+       sup->max_bud_bytes = cpu_to_le64(c->max_bud_bytes);
+       sup->log_lebs      = cpu_to_le32(c->log_lebs);
+       sup->lpt_lebs      = cpu_to_le32(c->lpt_lebs);
+       sup->orph_lebs     = cpu_to_le32(c->orph_lebs);
+       sup->jhead_cnt     = cpu_to_le32(c->jhead_cnt);
+       sup->fanout        = cpu_to_le32(c->fanout);
+       sup->lsave_cnt     = cpu_to_le32(c->lsave_cnt);
+       sup->fmt_version   = cpu_to_le32(ubifs_format_version());
+       sup->default_compr = cpu_to_le16(c->default_compr);
+       sup->rp_size       = cpu_to_le64(c->rp_size);
+       sup->time_gran     = cpu_to_le32(DEFAULT_TIME_GRAN);
+       sup->hash_algo     = cpu_to_le16(c->hash_algo);
+       uuid_generate_random(sup->uuid);
+
        if (verbose) {
                char s[40];
 
-               uuid_unparse_upper(sup.uuid, s);
+               uuid_unparse_upper(sup->uuid, s);
                printf("\tUUID:         %s\n", s);
        }
        if (c->big_lpt)
-               sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT);
+               sup->flags |= cpu_to_le32(UBIFS_FLG_BIGLPT);
        if (c->space_fixup)
-               sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP);
+               sup->flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP);
        if (c->double_hash)
-               sup.flags |= cpu_to_le32(UBIFS_FLG_DOUBLE_HASH);
+               sup->flags |= cpu_to_le32(UBIFS_FLG_DOUBLE_HASH);
        if (c->encrypted)
-               sup.flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
+               sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
+       if (authenticated()) {
+               sup->flags |= cpu_to_le32(UBIFS_FLG_AUTHENTICATION);
+               memcpy(sup->hash_mst, c->mst_hash, c->hash_len);
+       }
 
-       return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM);
+       prepare_node(sup, UBIFS_SB_NODE_SZ);
+
+       err = sign_superblock_node(sup);
+       if (err)
+               goto out;
+
+       sig = (void *)(sup + 1);
+       prepare_node(sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len));
+
+       len = do_pad(sig, UBIFS_SIG_NODE_SZ + le32_to_cpu(sig->len));
+
+       err = write_leb(UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ + len, sup);
+       if (err)
+               goto out;
+
+       err = 0;
+out:
+       free(buf);
+
+       return err;
 }
 
 /**
@@ -2592,6 +2681,11 @@ static int write_master(void)
        mst.total_dark   = cpu_to_le64(c->lst.total_dark);
        mst.leb_cnt      = cpu_to_le32(c->leb_cnt);
 
+       if (authenticated()) {
+               memcpy(mst.hash_root_idx, c->root_idx_hash, c->hash_len);
+               memcpy(mst.hash_lpt, c->lpt_hash, c->hash_len);
+       }
+
        err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM);
        if (err)
                return err;
@@ -2600,6 +2694,8 @@ static int write_master(void)
        if (err)
                return err;
 
+       mst_node_calc_hash(&mst, c->mst_hash);
+
        return 0;
 }
 
@@ -2864,6 +2960,10 @@ static int mkfs(void)
        if (err)
                goto out;
 
+       err = init_authentication();
+       if (err)
+               goto out;
+
        err = write_data();
        if (err)
                goto out;
@@ -2884,11 +2984,11 @@ static int mkfs(void)
        if (err)
                goto out;
 
-       err = write_super();
+       err = write_master();
        if (err)
                goto out;
 
-       err = write_master();
+       err = write_super();
        if (err)
                goto out;
 
index f1425c5af31a8414a50f76fe0d082939dbbdd184..569098425f136341730995d6ee0c2243a2db2386 100644 (file)
@@ -63,6 +63,7 @@
 #include "key.h"
 #include "lpt.h"
 #include "compr.h"
+#include "sign.h"
 
 /*
  * Compression flags are duplicated so that compr.c can compile without ubifs.h.
diff --git a/ubifs-utils/mkfs.ubifs/sign.c b/ubifs-utils/mkfs.ubifs/sign.c
new file mode 100644 (file)
index 0000000..b7ad7ef
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2018 Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Sascha Hauer
+ */
+
+#include "mkfs.ubifs.h"
+#include "common.h"
+
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <openssl/cms.h>
+#include <openssl/conf.h>
+#include <err.h>
+
+static struct ubifs_info *c = &info_;
+
+EVP_MD_CTX *hash_md;
+const EVP_MD *md;
+
+int authenticated(void)
+{
+       return c->hash_algo_name != NULL;
+}
+
+static int match_string(const char * const *array, size_t n, const char *string)
+{
+       int index;
+       const char *item;
+
+       for (index = 0; index < n; index++) {
+               item = array[index];
+               if (!item)
+                       break;
+               if (!strcmp(item, string))
+                       return index;
+       }
+
+       return -EINVAL;
+}
+
+#include <linux/hash_info.h>
+
+const char *const hash_algo_name[HASH_ALGO__LAST] = {
+       [HASH_ALGO_MD4]         = "md4",
+       [HASH_ALGO_MD5]         = "md5",
+       [HASH_ALGO_SHA1]        = "sha1",
+       [HASH_ALGO_RIPE_MD_160] = "rmd160",
+       [HASH_ALGO_SHA256]      = "sha256",
+       [HASH_ALGO_SHA384]      = "sha384",
+       [HASH_ALGO_SHA512]      = "sha512",
+       [HASH_ALGO_SHA224]      = "sha224",
+       [HASH_ALGO_RIPE_MD_128] = "rmd128",
+       [HASH_ALGO_RIPE_MD_256] = "rmd256",
+       [HASH_ALGO_RIPE_MD_320] = "rmd320",
+       [HASH_ALGO_WP_256]      = "wp256",
+       [HASH_ALGO_WP_384]      = "wp384",
+       [HASH_ALGO_WP_512]      = "wp512",
+       [HASH_ALGO_TGR_128]     = "tgr128",
+       [HASH_ALGO_TGR_160]     = "tgr160",
+       [HASH_ALGO_TGR_192]     = "tgr192",
+       [HASH_ALGO_SM3_256]     = "sm3-256",
+};
+
+static void display_openssl_errors(int l)
+{
+       const char *file;
+       char buf[120];
+       int e, line;
+
+       if (ERR_peek_error() == 0)
+               return;
+       fprintf(stderr, "At main.c:%d:\n", l);
+
+       while ((e = ERR_get_error_line(&file, &line))) {
+               ERR_error_string(e, buf);
+               fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
+       }
+}
+
+static void drain_openssl_errors(void)
+{
+       const char *file;
+       int line;
+
+       if (ERR_peek_error() == 0)
+               return;
+       while (ERR_get_error_line(&file, &line)) {}
+}
+
+#define ssl_err_msg(fmt, ...) ({                       \
+       display_openssl_errors(__LINE__);               \
+       err_msg(fmt, ## __VA_ARGS__);                   \
+       -1;                                             \
+})
+
+static const char *key_pass;
+
+static int pem_pw_cb(char *buf, int len, __attribute__((unused)) int w,
+                    __attribute__((unused)) void *v)
+{
+       int pwlen;
+
+       if (!key_pass)
+               return -1;
+
+       pwlen = strlen(key_pass);
+       if (pwlen >= len)
+               return -1;
+
+       strcpy(buf, key_pass);
+
+       /* If it's wrong, don't keep trying it. */
+       key_pass = NULL;
+
+       return pwlen;
+}
+
+static EVP_PKEY *read_private_key(const char *private_key_name, X509 **cert)
+{
+       EVP_PKEY *private_key = NULL;
+       int err;
+
+       *cert = NULL;
+
+       if (!strncmp(private_key_name, "pkcs11:", 7)) {
+               ENGINE *e;
+               struct {
+                       const char *url;
+                       X509 *cert;
+               } parms = {
+                       .url = private_key_name,
+               };
+
+               ENGINE_load_builtin_engines();
+               drain_openssl_errors();
+               e = ENGINE_by_id("pkcs11");
+               if (!e) {
+                       ssl_err_msg("Load PKCS#11 ENGINE");
+                       return NULL;
+               }
+
+               if (ENGINE_init(e)) {
+                       drain_openssl_errors();
+               } else {
+                       ssl_err_msg("ENGINE_init");
+                       return NULL;
+               }
+
+               if (key_pass)
+                       if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) {
+                               ssl_err_msg("Set PKCS#11 PIN");
+                               return NULL;
+                       }
+
+               private_key = ENGINE_load_private_key(e, private_key_name,
+                                                     NULL, NULL);
+
+               err = ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 0);
+               if (!err || !parms.cert) {
+                       ssl_err_msg("Load certificate");
+               }
+               *cert = parms.cert;
+               fprintf(stderr, "Using cert %p\n", *cert);
+       } else {
+               BIO *b;
+
+               b = BIO_new_file(private_key_name, "rb");
+               if (!b)
+                       goto out;
+
+               private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
+                                                     NULL);
+               BIO_free(b);
+       }
+out:
+       if (!private_key)
+               ssl_err_msg("failed opening private key %s", private_key_name);
+
+       return private_key;
+}
+
+static X509 *read_x509(const char *x509_name)
+{
+       unsigned char buf[2];
+       X509 *x509 = NULL;
+       BIO *b;
+       int n;
+
+       b = BIO_new_file(x509_name, "rb");
+       if (!b)
+               goto out;
+
+       /* Look at the first two bytes of the file to determine the encoding */
+       n = BIO_read(b, buf, 2);
+       if (n != 2) {
+               if (BIO_should_retry(b))
+                       err_msg("%s: Read wanted retry", x509_name);
+               if (n >= 0)
+                       err_msg("%s: Short read", x509_name);
+               goto out;
+       }
+
+       if (BIO_reset(b))
+               goto out;
+
+       if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+               /* Assume raw DER encoded X.509 */
+               x509 = d2i_X509_bio(b, NULL);
+       else
+               /* Assume PEM encoded X.509 */
+               x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
+       BIO_free(b);
+
+out:
+       if (!x509) {
+               ssl_err_msg("%s", x509_name);
+               return NULL;
+       }
+
+       return x509;
+}
+
+int sign_superblock_node(void *node)
+{
+       EVP_PKEY *private_key;
+       CMS_ContentInfo *cms = NULL;
+       X509 *cert = NULL;
+       BIO *bd, *bm;
+       void *obuf;
+       long len;
+       int ret;
+       void *pret;
+       struct ubifs_sig_node *sig = node + UBIFS_SB_NODE_SZ;
+
+       if (!authenticated())
+               return 0;
+
+       ERR_load_crypto_strings();
+       ERR_clear_error();
+
+       key_pass = getenv("MKFS_UBIFS_SIGN_PIN");
+
+       bm = BIO_new_mem_buf(node, UBIFS_SB_NODE_SZ);
+
+       private_key = read_private_key(c->auth_key_filename, &cert);
+       if (!private_key)
+               return -1;
+
+       if (!cert) {
+               if (!c->auth_cert_filename)
+                       return err_msg("authentication certificate not provided (--auth-cert)");
+               cert = read_x509(c->auth_cert_filename);
+       }
+
+       if (!cert)
+               return -1;
+
+       OpenSSL_add_all_digests();
+       display_openssl_errors(__LINE__);
+
+       cms = CMS_sign(NULL, NULL, NULL, NULL,
+                      CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
+                      CMS_DETACHED | CMS_STREAM);
+       if (!cms)
+               return err_msg("CMS_sign failed");
+
+       pret = CMS_add1_signer(cms, cert, private_key, md,
+                             CMS_NOCERTS | CMS_BINARY |
+                             CMS_NOSMIMECAP | CMS_NOATTR);
+       if (!pret)
+               return err_msg("CMS_add1_signer failed");
+
+       ret = CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY);
+       if (!ret)
+               return err_msg("CMS_final failed");
+
+       bd = BIO_new(BIO_s_mem());
+
+       ret = i2d_CMS_bio_stream(bd, cms, NULL, 0);
+       if (!ret)
+               return err_msg("i2d_CMS_bio_stream failed");
+
+       len = BIO_get_mem_data(bd, &obuf);
+
+       sig->type = UBIFS_SIGNATURE_TYPE_PKCS7;
+       sig->len = cpu_to_le32(len);
+       sig->ch.node_type  = UBIFS_SIG_NODE;
+
+       memcpy(sig + 1, obuf, len);
+
+       BIO_free(bd);
+       BIO_free(bm);
+
+       return 0;
+}
+
+/**
+ * ubifs_node_calc_hash - calculate the hash of a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @hash: the returned hash
+ */
+void ubifs_node_calc_hash(const void *node, uint8_t *hash)
+{
+       const struct ubifs_ch *ch = node;
+       unsigned int md_len;
+
+       if (!authenticated())
+               return;
+
+       EVP_DigestInit_ex(hash_md, md, NULL);
+       EVP_DigestUpdate(hash_md, node, le32_to_cpu(ch->len));
+       EVP_DigestFinal_ex(hash_md, hash, &md_len);
+}
+
+/**
+ * mst_node_calc_hash - calculate the hash of a UBIFS master node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @hash: the returned hash
+ */
+void mst_node_calc_hash(const void *node, uint8_t *hash)
+{
+       unsigned int md_len;
+
+       if (!authenticated())
+               return;
+
+       EVP_DigestInit_ex(hash_md, md, NULL);
+       EVP_DigestUpdate(hash_md, node + sizeof(struct ubifs_ch),
+                        UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch));
+       EVP_DigestFinal_ex(hash_md, hash, &md_len);
+}
+
+void hash_digest_init(void)
+{
+       if (!authenticated())
+               return;
+
+       EVP_DigestInit_ex(hash_md, md, NULL);
+}
+
+void hash_digest_update(const void *buf, int len)
+{
+       if (!authenticated())
+               return;
+
+       EVP_DigestUpdate(hash_md, buf, len);
+}
+
+void hash_digest_final(void *hash, unsigned int *len)
+{
+       if (!authenticated())
+               return;
+
+       EVP_DigestFinal_ex(hash_md, hash, len);
+}
+
+int init_authentication(void)
+{
+       int hash_algo;
+
+       if (!c->auth_key_filename && !c->auth_cert_filename && !c->hash_algo_name)
+               return 0;
+
+       if (!c->auth_key_filename)
+               return err_msg("authentication key not given (--auth-key)");
+
+       if (!c->hash_algo_name)
+               return err_msg("Hash algorithm not given (--hash-algo)");
+
+       OPENSSL_no_config();
+       OpenSSL_add_all_algorithms();
+       ERR_load_crypto_strings();
+
+       md = EVP_get_digestbyname(c->hash_algo_name);
+       if (!md)
+               return err_msg("Unknown message digest %s", c->hash_algo_name);
+
+       hash_md = EVP_MD_CTX_create();
+       c->hash_len = EVP_MD_size(md);
+
+       hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST, c->hash_algo_name);
+       if (hash_algo < 0)
+               return err_msg("Unsupported message digest %s", c->hash_algo_name);
+
+       c->hash_algo = hash_algo;
+
+       return 0;
+}
diff --git a/ubifs-utils/mkfs.ubifs/sign.h b/ubifs-utils/mkfs.ubifs/sign.h
new file mode 100644 (file)
index 0000000..fe9fdd8
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Sascha Hauer
+ */
+
+#ifndef __UBIFS_SIGN_H__
+#define __UBIFS_SIGN_H__
+
+#ifdef WITH_CRYPTO
+#include <openssl/evp.h>
+
+void ubifs_node_calc_hash(const void *node, uint8_t *hash);
+void mst_node_calc_hash(const void *node, uint8_t *hash);
+void hash_digest_init(void);
+void hash_digest_update(const void *buf, int len);
+void hash_digest_final(void *hash, unsigned int *len);
+int init_authentication(void);
+int sign_superblock_node(void *node);
+int authenticated(void);
+
+extern EVP_MD_CTX *hash_md;
+extern const EVP_MD *md;
+
+#else
+static inline void ubifs_node_calc_hash(__attribute__((unused)) const void *node,
+                                       __attribute__((unused)) uint8_t *hash)
+{
+}
+
+static inline void mst_node_calc_hash(__attribute__((unused)) const void *node,
+                                     __attribute__((unused)) uint8_t *hash)
+{
+}
+
+static inline void hash_digest_init(void)
+{
+}
+
+static inline void hash_digest_update(__attribute__((unused)) const void *buf,
+                                     __attribute__((unused)) int len)
+{
+}
+
+static inline void hash_digest_final(__attribute__((unused)) void *hash,
+                                    __attribute__((unused)) unsigned int *len)
+{
+}
+
+static inline int init_authentication(void)
+{
+       return 0;
+}
+
+static inline int sign_superblock_node(__attribute__((unused)) void *node)
+{
+       return 0;
+}
+
+static inline int authenticated(void)
+{
+       return 0;
+}
+
+#endif
+
+#endif /* __UBIFS_SIGN_H__ */
index c26d0944ac50bc7d3577e0e5ba679b1ea6b35886..55937cee783d60c0bbb5ea99624a04f88ad4bc39 100644 (file)
@@ -342,6 +342,15 @@ struct ubifs_znode
  * @lsave_offs: offset of LPT's save table
  * @lsave: LPT's save table
  * @lscan_lnum: LEB number of last LPT scan
+ *
+ * @hash_algo_name: the name of the hashing algorithm to use
+ * @hash_algo: The hash algo number (from include/linux/hash_info.h)
+ * @auth_key_filename: authentication key file name
+ * @x509_filename: x509 certificate file name for authentication
+ * @hash_len: the length of the hash
+ * @root_idx_hash: The hash of the root index node
+ * @lpt_hash: The hash of the LPT
+ * @mst_hash: The hash of the master node
  */
 struct ubifs_info
 {
@@ -423,6 +432,14 @@ struct ubifs_info
        int *lsave;
        int lscan_lnum;
 
+       char *hash_algo_name;
+       int hash_algo;
+       char *auth_key_filename;
+       char *auth_cert_filename;
+       int hash_len;
+       uint8_t root_idx_hash[UBIFS_MAX_HASH_LEN];
+       uint8_t lpt_hash[UBIFS_MAX_HASH_LEN];
+       uint8_t mst_hash[UBIFS_MAX_HASH_LEN];
 };
 
 /**
@@ -432,7 +449,8 @@ struct ubifs_info
  */
 static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
 {
-       return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt;
+       return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len + c->hash_len)
+                                   * child_cnt;
 }
 
 /**
@@ -447,7 +465,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
                                      int bnum)
 {
        return (struct ubifs_branch *)((void *)idx->branches +
-                                      (UBIFS_BRANCH_SZ + c->key_len) * bnum);
+                                      (UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
 }
 
 #endif /* __UBIFS_H__ */