diff options
| author | Sascha Hauer <s.hauer@pengutronix.de> | 2019-08-06 12:49:28 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-08-19 09:27:00 +0200 | 
| commit | a739b59efe7996e3bdcbe8b17743dc05ac7c110a (patch) | |
| tree | 863ad65c25a5bcb9be3e5a170465ddc384d7b87b /ubifs-utils | |
| parent | 3ef262739a826deb27262929b88f36db6f923e7f (diff) | |
mkfs.ubifs: Add authentication support
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>
Diffstat (limited to 'ubifs-utils')
| -rw-r--r-- | ubifs-utils/Makemodule.am | 3 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/lpt.c | 12 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 172 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/mkfs.ubifs.h | 1 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/sign.c | 409 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/sign.h | 80 | ||||
| -rw-r--r-- | ubifs-utils/mkfs.ubifs/ubifs.h | 22 | 
7 files changed, 660 insertions, 39 deletions
| diff --git a/ubifs-utils/Makemodule.am b/ubifs-utils/Makemodule.am index 164ce09..0c64445 100644 --- a/ubifs-utils/Makemodule.am +++ b/ubifs-utils/Makemodule.am @@ -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 diff --git a/ubifs-utils/mkfs.ubifs/lpt.c b/ubifs-utils/mkfs.ubifs/lpt.c index 6aa0b88..7ee739a 100644 --- a/ubifs-utils/mkfs.ubifs/lpt.c +++ b/ubifs-utils/mkfs.ubifs/lpt.c @@ -22,6 +22,10 @@  #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) { diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c index 58ebf8c..5748aaa 100644 --- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c @@ -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; diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h index f1425c5..5690984 100644 --- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h @@ -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 index 0000000..b7ad7ef --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/sign.c @@ -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 index 0000000..fe9fdd8 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/sign.h @@ -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__ */ diff --git a/ubifs-utils/mkfs.ubifs/ubifs.h b/ubifs-utils/mkfs.ubifs/ubifs.h index c26d094..55937ce 100644 --- a/ubifs-utils/mkfs.ubifs/ubifs.h +++ b/ubifs-utils/mkfs.ubifs/ubifs.h @@ -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__ */ | 
